diff --git a/.changes/2.10.10.json b/.changes/2.10.10.json new file mode 100644 index 000000000000..30862214c3a5 --- /dev/null +++ b/.changes/2.10.10.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.10", + "date": "2019-11-06", + "entries": [ + { + "type": "feature", + "category": "AWS Budgets", + "description": "Documentation updates for budgets to track Savings Plans utilization and coverage" + }, + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "This launch provides customers with access to Savings Plans management APIs." + }, + { + "type": "feature", + "category": "Amazon Elastic File System", + "description": "EFS customers can select a lifecycle policy that automatically moves files that have not been accessed for 7 days into the EFS Infrequent Access (EFS IA) storage class. EFS IA provides price/performance that is cost-optimized for files that are not accessed every day." + }, + { + "type": "feature", + "category": "AWS Signer", + "description": "This release adds support for tagging code-signing profiles in AWS Signer." + }, + { + "type": "feature", + "category": "AWS Savings Plans", + "description": "This is the first release of Savings Plans, a new flexible pricing model that offers low prices on Amazon EC2 and AWS Fargate usage." + }, + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "Add support for Build Number, Secrets Manager and Exported Environment Variables." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.11.json b/.changes/2.10.11.json new file mode 100644 index 000000000000..d00a706f5582 --- /dev/null +++ b/.changes/2.10.11.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.11", + "date": "2019-11-06", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Added the web identity credentials provider to the default credential chain" + }, + { + "type": "feature", + "category": "AWS Savings Plans", + "description": "This is the first release of Savings Plans, a new flexible pricing model that offers low prices on Amazon EC2 and AWS Fargate usage." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.12.json b/.changes/2.10.12.json new file mode 100644 index 000000000000..f5493dd810b4 --- /dev/null +++ b/.changes/2.10.12.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.12", + "date": "2019-11-07", + "entries": [ + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "AWS Systems Manager Session Manager target length increased to 400." + }, + { + "type": "feature", + "category": "Netty NIO HTTP Client", + "description": "Switch from setting the absolute URI in HTTP requests with no `Host` header to setting the absolute request path and query paramters and a `Host` header." + }, + { + "type": "feature", + "category": "AWS SSO OIDC", + "description": "This is an initial release of AWS Single Sign-On OAuth device code authorization service." + }, + { + "type": "feature", + "category": "AWS S3", + "description": "Added support for presignPutObject in S3Presigner." + }, + { + "type": "feature", + "category": "AWS Single Sign-On", + "description": "This is an initial release of AWS Single Sign-On (SSO) end-user access. This release adds support for accessing AWS accounts assigned in AWS SSO using short term credentials." + }, + { + "type": "feature", + "category": "Amazon Comprehend", + "description": "This release adds new languages (ar, hi, ko, ja, zh, zh-TW) for Amazon Comprehend's DetectSentiment, DetectEntities, DetectKeyPhrases, BatchDetectSentiment, BatchDetectEntities and BatchDetectKeyPhrases APIs" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.13.json b/.changes/2.10.13.json new file mode 100644 index 000000000000..962c28a5fa65 --- /dev/null +++ b/.changes/2.10.13.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.13", + "date": "2019-11-08", + "entries": [ + { + "type": "feature", + "category": "Amazon EC2 Container Registry", + "description": "This release contains ticket fixes for Amazon ECR." + }, + { + "type": "feature", + "category": "Amazon Cognito Identity", + "description": "This release adds support for disabling classic flow." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.14.json b/.changes/2.10.14.json new file mode 100644 index 000000000000..ff057f494e75 --- /dev/null +++ b/.changes/2.10.14.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.14", + "date": "2019-11-11", + "entries": [ + { + "type": "feature", + "category": "Amazon Polly", + "description": "Add `PollyPresigner` which enables support for presigning `SynthesizeSpeech` requests." + }, + { + "type": "feature", + "category": "AWS CloudFormation", + "description": "The Resource Import feature enables customers to import existing AWS resources into new or existing CloudFormation Stacks." + }, + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "This launch provides customers with access to GetCostAndUsageWithResources API." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.15.json b/.changes/2.10.15.json new file mode 100644 index 000000000000..d4b7f744262d --- /dev/null +++ b/.changes/2.10.15.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.15", + "date": "2019-11-12", + "entries": [ + { + "type": "feature", + "category": "AWS Marketplace Catalog Service", + "description": "This is the first release for the AWS Marketplace Catalog service which allows you to list, describe and manage change requests on your published entities on AWS Marketplace." + }, + { + "type": "feature", + "category": "Amazon DynamoDB", + "description": "Amazon DynamoDB enables you to restore your data to a new DynamoDB table using a point-in-time or on-demand backup. You now can modify the settings on the new restored table. Specifically, you can exclude some or all of the local and global secondary indexes from being created with the restored table. In addition, you can change the billing mode and provisioned capacity settings." + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "With this release, Amazon Transcribe now supports transcriptions from audio sources in Welsh English (en-WL), Scottish English(en-AB), Irish English(en-IE), Farsi(fa-IR), Tamil(ta-IN), Indonesian(id-ID), Portuguese (pt-PT), Dutch(nl-NL)." + }, + { + "type": "feature", + "category": "Elastic Load Balancing", + "description": "You can configure your Application Load Balancer to either drop invalid header fields or forward them to targets." + }, + { + "type": "feature", + "category": "AWS CodePipeline", + "description": "AWS CodePipeline now supports the use of variables in action configuration." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.16.json b/.changes/2.10.16.json new file mode 100644 index 000000000000..3214de451465 --- /dev/null +++ b/.changes/2.10.16.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.16", + "date": "2019-11-13", + "entries": [ + { + "type": "feature", + "category": "Amazon Simple Email Service", + "description": "This is the first release of version 2 of the Amazon SES API. You can use this API to configure your Amazon SES account, and to send email. This API extends the functionality that exists in the previous version of the Amazon SES API." + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "This release adds the custom fields definition support in the index definition for AWS IoT Fleet Indexing Service. Custom fields can be used as an aggregation field to run aggregations with both existing GetStatistics API and newly added GetCardinality, GetPercentiles APIs. GetStatistics will return all statistics (min/max/sum/avg/count...) with this release. For more information, please refer to our latest documentation: https://docs.aws.amazon.com/iot/latest/developerguide/iot-indexing.html" + }, + { + "type": "feature", + "category": "Amazon Data Lifecycle Manager", + "description": "You can now add tags to a lifecycle policy in Data Lifecycle Manager (DLM). Tags allow you to categorize your policies in different ways, such as by department, purpose or owner. You can also enable resource level permissions based on tags to set access control on ability to modify or delete a tagged policy." + }, + { + "type": "feature", + "category": "Amazon CloudSearch", + "description": "Amazon CloudSearch domains let you require that all traffic to the domain arrive over HTTPS. This security feature helps you block clients that send unencrypted requests to the domain." + }, + { + "type": "feature", + "category": "AWS Data Exchange", + "description": "Introducing AWS Data Exchange, a service that makes it easy for AWS customers to securely create, manage, access, and exchange data sets in the cloud." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.17.json b/.changes/2.10.17.json new file mode 100644 index 000000000000..d8b7ac2d3058 --- /dev/null +++ b/.changes/2.10.17.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.17", + "date": "2019-11-14", + "entries": [ + { + "type": "feature", + "category": "AWSMarketplace Metering", + "description": "Added CustomerNotEntitledException in MeterUsage API for Container use case." + }, + { + "type": "feature", + "category": "Amazon Connect Service", + "description": "This release enhances the existing user management APIs and adds 3 new APIs - TagResource, UntagResource, and ListTagsForResource to support tagging Amazon Connect users, which facilitates more granular access controls for Amazon Connect users within an Amazon Connect instance. You can learn more about the new APIs here: https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "Updates support for adding attachments to Systems Manager Automation documents" + }, + { + "type": "feature", + "category": "Amazon Cognito Identity Provider", + "description": "This release adds a new setting at user pool client to prevent user existence related errors during authentication, confirmation, and password recovery related operations. This release also adds support to enable or disable specific authentication flows for a user pool client." + }, + { + "type": "feature", + "category": "Amazon Personalize", + "description": "Amazon Personalize: Adds ability to get batch recommendations by creating a batch inference job." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.18.json b/.changes/2.10.18.json new file mode 100644 index 000000000000..a8ea4c81e4af --- /dev/null +++ b/.changes/2.10.18.json @@ -0,0 +1,66 @@ +{ + "version": "2.10.18", + "date": "2019-11-15", + "entries": [ + { + "type": "feature", + "category": "Amazon CloudWatch Logs", + "description": "Documentation updates for logs" + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "This release updates AWS Systems Manager Parameter Store documentation for the enhanced search capability." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "You can now add tags while copying snapshots. Previously, a user had to first copy the snapshot and then add tags to the copied snapshot manually. Moving forward, you can specify the list of tags you wish to be applied to the copied snapshot as a parameter on the Copy Snapshot API." + }, + { + "type": "feature", + "category": "Amazon Chime", + "description": "This release adds support for Chime Room Management APIs" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "When SdkException or one of its children is created without a 'message', inherit the message from the exception 'cause' (if any). This should reduce the chance of an exception being raised by the SDK with a null message." + }, + { + "type": "feature", + "category": "Amazon Cognito Identity Provider", + "description": "This release adds a new option in the User Pool to allow specifying sender's name in the emails sent by Amazon Cognito. This release also adds support to add SES Configuration Set to the emails sent by Amazon Cognito." + }, + { + "type": "feature", + "category": "Amazon WorkSpaces", + "description": "Added APIs to register your directories with Amazon WorkSpaces and to modify directory details." + }, + { + "type": "feature", + "category": "Amazon Elastic MapReduce", + "description": "Access to the cluster ARN makes it easier for you to author resource-level permissions policies in AWS Identity and Access Management. To simplify the process of obtaining the cluster ARN, Amazon EMR has added a new field containing the cluster ARN to all API responses that include the cluster ID." + }, + { + "type": "feature", + "category": "AWS Elemental MediaConvert", + "description": "AWS Elemental MediaConvert SDK has added support for DolbyVision encoding, and SCTE35 & ESAM insertion to DASH ISO EMSG." + }, + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Introducing Amazon EKS managed node groups, a new feature that lets you easily provision worker nodes for Amazon EKS clusters and keep them up to date using the Amazon EKS management console, CLI, and APIs." + }, + { + "type": "feature", + "category": "Elastic Load Balancing", + "description": "Documentation-only change to the default value of the routing.http.drop_invalid_header_fields.enabled attribute." + }, + { + "type": "feature", + "category": "Amazon GuardDuty", + "description": "This release includes new operations related to findings export, including: CreatePublishingDestination, UpdatePublishingDestination, DescribePublishingDestination, DeletePublishingDestination and ListPublishingDestinations." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.19.json b/.changes/2.10.19.json new file mode 100644 index 000000000000..fc378ff7f1c8 --- /dev/null +++ b/.changes/2.10.19.json @@ -0,0 +1,51 @@ +{ + "version": "2.10.19", + "date": "2019-11-18", + "entries": [ + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "add EstimatedOnDemandCostWithCurrentCommitment to GetSavingsPlansPurchaseRecommendationRequest API" + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "The release contains new API and API changes for AWS Systems Manager Explorer product." + }, + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Update default connectionMaxIdleTimeout of NettyNioAsyncClient to 5 seconds" + }, + { + "type": "feature", + "category": "Amazon Simple Storage Service", + "description": "Added support for S3 Replication for existing objects. This release allows customers who have requested and been granted access to replicate existing S3 objects across buckets." + }, + { + "type": "feature", + "category": "Amazon SageMaker Runtime", + "description": "Amazon SageMaker Runtime now supports a new TargetModel header to invoke a specific model hosted on multi model endpoints." + }, + { + "type": "feature", + "category": "AWS CloudFormation", + "description": "This release introduces APIs for the CloudFormation Registry, a new service to submit and discover resource providers with which you can manage third-party resources natively in CloudFormation." + }, + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "Documentation updates for rds" + }, + { + "type": "feature", + "category": "Amazon SageMaker Service", + "description": "Amazon SageMaker now supports multi-model endpoints to host multiple models on an endpoint using a single inference container." + }, + { + "type": "feature", + "category": "Amazon Pinpoint", + "description": "This release of the Amazon Pinpoint API introduces support for using and managing message templates for messages that are sent through the voice channel. It also introduces support for specifying default values for message variables in message templates." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.20.json b/.changes/2.10.20.json new file mode 100644 index 000000000000..ed52fb1dd804 --- /dev/null +++ b/.changes/2.10.20.json @@ -0,0 +1,51 @@ +{ + "version": "2.10.20", + "date": "2019-11-19", + "entries": [ + { + "type": "feature", + "category": "AWS Identity and Access Management", + "description": "IAM reports the timestamp when a role's credentials were last used to make an AWS request. This helps you identify unused roles and remove them confidently from your AWS accounts." + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "As part of this release, we are extending the capability of AWS IoT Rules Engine to send messages directly to customer's own web services/applications. Customers can now create topic rules with HTTP actions to route messages from IoT Core directly to URL's that they own. Ownership is proved by creating and confirming topic rule destinations." + }, + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "Add support for ARM and GPU-enhanced build environments and a new SSD-backed Linux compute type with additional CPU and memory in CodeBuild" + }, + { + "type": "feature", + "category": "AWS Config", + "description": "AWSConfig launches support for conformance packs. A conformance pack is a new resource type that allows you to package a collection of Config rules and remediation actions into a single entity. You can create and deploy conformance packs into your account or across all accounts in your organization" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds support for RunInstances to specify the metadata options for new instances; adds a new API, ModifyInstanceMetadataOptions, which lets you modify the metadata options for a running or stopped instance; and adds support for CreateCustomerGateway to specify a device name." + }, + { + "type": "feature", + "category": "Elastic Load Balancing", + "description": "This release allows forward actions on Application Load Balancers to route requests to multiple target groups, based on the weight you specify for each target group." + }, + { + "type": "feature", + "category": "AWS Lambda", + "description": "This release provides three new runtimes to support Node.js 12 (initially 12.13.0), Python 3.8 and Java 11." + }, + { + "type": "feature", + "category": "AWS CloudFormation", + "description": "This release of AWS CloudFormation StackSets enables users to detect drift on a stack set and the stack instances that belong to that stack set." + }, + { + "type": "feature", + "category": "Auto Scaling", + "description": "Amazon EC2 Auto Scaling now supports Instance Weighting and Max Instance Lifetime. Instance Weighting allows specifying the capacity units for each instance type included in the MixedInstancesPolicy and how they would contribute to your application's performance. Max Instance Lifetime allows specifying the maximum length of time that an instance can be in service. If any instances are approaching this limit, Amazon EC2 Auto Scaling gradually replaces them." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.21.json b/.changes/2.10.21.json new file mode 100644 index 000000000000..440fa04fbf2f --- /dev/null +++ b/.changes/2.10.21.json @@ -0,0 +1,91 @@ +{ + "version": "2.10.21", + "date": "2019-11-20", + "entries": [ + { + "type": "feature", + "category": "AWS Application Discovery Service", + "description": "New exception type for use with Migration Hub home region" + }, + { + "type": "feature", + "category": "Amazon EC2 Container Service", + "description": "Added support for CPU and memory task-level overrides on the RunTask and StartTask APIs. Added location information to Tasks." + }, + { + "type": "feature", + "category": "Amazon Data Lifecycle Manager", + "description": "DLM now supports Fast Snapshot Restore. You can enable Fast Restore on snapshots created by DLM, provide the AZs and the number of snapshots to be enabled with this capability." + }, + { + "type": "feature", + "category": "Amazon Kinesis Firehose", + "description": "With this release, Amazon Kinesis Data Firehose allows server side encryption with customer managed CMKs. Customer managed CMKs ( \"Customer Master Keys\") are AWS Key Management Service (KMS) keys that are fully managed by the customer. With customer managed CMKs, customers can establish and maintain their key policies, IAM policies, rotating policies and add tags. For more information about AWS KMS and CMKs, please refer to: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html. Please refer to the following link to create CMKs: https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-create-cmk.html" + }, + { + "type": "feature", + "category": "Amazon Simple Storage Service", + "description": "This release introduces support for Amazon S3 Replication Time Control, a new feature of S3 Replication that provides a predictable replication time backed by a Service Level Agreement. S3 Replication Time Control helps customers meet compliance or business requirements for data replication, and provides visibility into the replication process with new Amazon CloudWatch Metrics." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release of Amazon Elastic Compute Cloud (Amazon EC2) introduces support for Amazon Elastic Block Store (Amazon EBS) fast snapshot restores." + }, + { + "type": "feature", + "category": "AWS CloudTrail", + "description": "1. This release adds two new APIs, GetInsightSelectors and PutInsightSelectors, which let you configure CloudTrail Insights event delivery on a trail. An Insights event is a new type of event that is generated when CloudTrail detects unusual activity in your AWS account. In this release, only \"ApiCallRateInsight\" is a supported Insights event type. 2. This release also adds the new \"ExcludeManagementEventSource\" option to the existing PutEventSelectors API. This field currently supports only AWS Key Management Services." + }, + { + "type": "feature", + "category": "AWS Migration Hub", + "description": "New exception type for use with Migration Hub home region" + }, + { + "type": "feature", + "category": "Amazon QuickSight", + "description": "Amazon QuickSight now supports programmatic creation and management of data sources, data sets, dashboards and templates with new APIs. Templates hold dashboard metadata, and can be used to create copies connected to the same or different dataset as required. Also included in this release are APIs for SPICE ingestions, fine-grained access control over AWS resources using AWS Identity and Access Management (IAM) policies, as well AWS tagging. APIs are supported for both Standard and Enterprise Edition, with edition-specific support for specific functionality." + }, + { + "type": "feature", + "category": "AWS Storage Gateway", + "description": "The new DescribeAvailabilityMonitorTest API provides the results of the most recent High Availability monitoring test. The new StartAvailabilityMonitorTest API verifies the storage gateway is configured for High Availability monitoring. The new ActiveDirectoryStatus response element has been added to the DescribeSMBSettings and JoinDomain APIs to indicate the status of the gateway after the most recent JoinDomain operation. The new TimeoutInSeconds parameter of the JoinDomain API allows for the configuration of the timeout in which the JoinDomain operation must complete." + }, + { + "type": "feature", + "category": "AWS Elemental MediaStore", + "description": "This release fixes a broken link in the SDK documentation." + }, + { + "type": "feature", + "category": "Amazon Chime", + "description": "Adds APIs to create and manage meeting session resources for the Amazon Chime SDK" + }, + { + "type": "feature", + "category": "AWS Migration Hub Config", + "description": "AWS Migration Hub Config Service allows you to get and set the Migration Hub home region for use with AWS Migration Hub and Application Discovery Service" + }, + { + "type": "feature", + "category": "AWS DataSync", + "description": "Update to configure task to run periodically on a schedule" + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "With this release Amazon Transcribe enables alternative transcriptions so that you can see different interpretations of transcribed audio." + }, + { + "type": "feature", + "category": "Amazon FSx", + "description": "Announcing a Multi-AZ deployment type for Amazon FSx for Windows File Server, providing fully-managed Windows file storage with high availability and redundancy across multiple AWS Availability Zones." + }, + { + "type": "feature", + "category": "AWS CodeCommit", + "description": "This release adds support for creating pull request approval rules and pull request approval rule templates in AWS CodeCommit. This allows developers to block merges of pull requests, contingent on the approval rules being satisfiied." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.22.json b/.changes/2.10.22.json new file mode 100644 index 000000000000..0117625e976a --- /dev/null +++ b/.changes/2.10.22.json @@ -0,0 +1,71 @@ +{ + "version": "2.10.22", + "date": "2019-11-21", + "entries": [ + { + "type": "feature", + "category": "AWS AppSync", + "description": "AppSync: AWS AppSync now supports the ability to add, configure, and maintain caching for your AWS AppSync GraphQL API." + }, + { + "type": "feature", + "category": "Amazon Connect Service", + "description": "This release adds a new API: StartChatContact. You can use it to programmatically start a chat on the specified Amazon Connect instance. Learn more here: https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html" + }, + { + "type": "feature", + "category": "Amazon Connect Participant Service", + "description": "This release adds 5 new APIs: CreateParticipantConnection, DisconnectParticipant, GetTranscript, SendEvent, and SendMessage. For Amazon Connect chat, you can use them to programmatically perform participant actions on the configured Amazon Connect instance. Learn more here: https://docs.aws.amazon.com/connect-participant/latest/APIReference/Welcome.html" + }, + { + "type": "feature", + "category": "Amazon DynamoDB", + "description": "With this release, you can convert an existing Amazon DynamoDB table to a global table by adding replicas in other AWS Regions." + }, + { + "type": "feature", + "category": "Amazon Lex Model Building Service", + "description": "Amazon Lex now supports Sentiment Analysis" + }, + { + "type": "feature", + "category": "Amazon Lex Runtime Service", + "description": "Amazon Lex now supports Sentiment Analysis" + }, + { + "type": "feature", + "category": "AWSMarketplace Metering", + "description": "Documentation updates for the AWS Marketplace Metering Service." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "The release contains new API and API changes for AWS Systems Manager Explorer product." + }, + { + "type": "feature", + "category": "AWS Config", + "description": "AWS Config launches Custom Configuration Items. A new feature which allows customers to publish resource configuration for third-party resources, custom, or on-premises servers." + }, + { + "type": "feature", + "category": "AWS Amplify", + "description": "This release of AWS Amplify Console introduces support for backend environments. Backend environments are containers for AWS deployments. Each environment is a collection of AWS resources." + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "With this release, Amazon Transcribe now supports transcriptions from audio sources in Hebrew (he-IL), Swiss German (de-CH), Japanese (ja-JP), Turkish (tr-TR), Arabic-Gulf (ar-AE), Malay (ms-MY), Telugu (te-IN)" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds support for attaching AWS License Manager Configurations to Amazon Machine Image (AMI) using ImportImage API; and adds support for running different instance sizes on EC2 Dedicated Hosts" + }, + { + "type": "feature", + "category": "AWS Glue", + "description": "This release adds support for Glue 1.0 compatible ML Transforms." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.23.json b/.changes/2.10.23.json new file mode 100644 index 000000000000..b80453b65e8b --- /dev/null +++ b/.changes/2.10.23.json @@ -0,0 +1,71 @@ +{ + "version": "2.10.23", + "date": "2019-11-22", + "entries": [ + { + "type": "feature", + "category": "AWS Certificate Manager", + "description": "This release adds support for Tag-Based IAM for AWS Certificate Manager and adding tags to certificates upon creation." + }, + { + "type": "feature", + "category": "Amazon Simple Notification Service", + "description": "Added documentation for the dead-letter queue feature." + }, + { + "type": "feature", + "category": "Amazon Forecast Service", + "description": "This release adds two key updates to existing APIs. 1. Amazon Forecast can now generate forecasts in any quantile using the optional parameter forecastTypes in the CreateForecast API and 2. You can get additional details (metrics and relevant error messages) on your AutoML runs using the DescribePredictor and GetAccuracyMetrics APIs." + }, + { + "type": "feature", + "category": "AWS Auto Scaling Plans", + "description": "Update default endpoint for AWS Auto Scaling." + }, + { + "type": "feature", + "category": "Application Auto Scaling", + "description": "Update default endpoint for Application Auto Scaling." + }, + { + "type": "feature", + "category": "Amazon Elastic MapReduce", + "description": "Amazon EMR adds support for concurrent step execution and cancelling running steps. Amazon EMR has added a new Outpost ARN field in the ListCluster and DescribeCluster API responses that is populated for clusters launched in an AWS Outpost subnet." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds two new APIs (DescribeInstanceTypes and DescribeInstanceTypeOfferings) that give customers access to instance type attributes and regional and zonal offerings." + }, + { + "type": "feature", + "category": "AWS Elemental MediaPackage VOD", + "description": "Includes the submission time of Asset ingestion request in the API response for Create/List/Describe Assets." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "The ProcessCredentialsProvider now supports credential files up to 64 KB by default through an increase of the processOutputLimit from 1024 bytes to 64000 bytes." + }, + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "Add Canonical ARN to LogsLocation." + }, + { + "type": "feature", + "category": "AWS Security Token Service", + "description": "Support tagging for STS sessions and tag based access control for the STS APIs" + }, + { + "type": "feature", + "category": "Amazon Rekognition", + "description": "This release adds enhanced face filtering support to the IndexFaces API operation, and introduces face filtering for CompareFaces and SearchFacesByImage API operations." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "Add RebootOption and LastNoRebootInstallOperationTime for DescribeInstancePatchStates and DescribeInstancePatchStatesForPatchGroup API" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.24.json b/.changes/2.10.24.json new file mode 100644 index 000000000000..ea4f4eafb49f --- /dev/null +++ b/.changes/2.10.24.json @@ -0,0 +1,151 @@ +{ + "version": "2.10.24", + "date": "2019-11-25", + "entries": [ + { + "type": "feature", + "category": "AWS Elemental MediaPackage VOD", + "description": "Adds a domain name to PackagingGroups, representing the fully qualified domain name for Assets created in the group." + }, + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "This launch provides customers with access to Cost Category Public Beta APIs." + }, + { + "type": "feature", + "category": "AWS Resource Access Manager", + "description": "AWS RAM provides new APIs to view the permissions granted to principals in a resource share. This release also creates corresponding resource shares for supported services that use resource policies, as well as an API to promote them to standard shares that can be managed in RAM." + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "This release adds: 1) APIs for fleet provisioning claim and template, 2) endpoint configuration and custom domains, 3) support for enhanced custom authentication, d) support for 4 additional audit checks: Device and CA certificate key quality checks, IoT role alias over-permissive check and IoT role alias access to unused services check, 5) extended capability of AWS IoT Rules Engine to support IoT SiteWise rule action. The IoT SiteWise rule action lets you send messages from IoT sensors and applications to IoT SiteWise asset properties" + }, + { + "type": "feature", + "category": "Elastic Load Balancing", + "description": "This release of Elastic Load Balancing V2 adds new subnet features for Network Load Balancers and a new routing algorithm for Application Load Balancers." + }, + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "CodeBuild adds support for test reporting" + }, + { + "type": "feature", + "category": "AWS Elemental MediaConvert", + "description": "AWS Elemental MediaConvert SDK has added support for 8K outputs and support for QuickTime Animation Codec (RLE) inputs." + }, + { + "type": "feature", + "category": "AWS Elemental MediaLive", + "description": "AWS Elemental MediaLive now supports the ability to create a multiple program transport stream (MPTS)." + }, + { + "type": "feature", + "category": "Amazon Comprehend", + "description": "Amazon Comprehend now supports real-time analysis with Custom Classification" + }, + { + "type": "feature", + "category": "Amazon Cognito Identity Provider", + "description": "Amazon Cognito Userpools now supports Sign in with Apple as an Identity Provider." + }, + { + "type": "feature", + "category": "AWS Lambda", + "description": "Added the function state and update status to the output of GetFunctionConfiguration and other actions. Check the state information to ensure that a function is ready before you perform operations on it. Functions take time to become ready when you connect them to a VPC.Added the EventInvokeConfig type and operations to configure error handling options for asynchronous invocation. Use PutFunctionEventInvokeConfig to configure the number of retries and the maximum age of events when you invoke the function asynchronously.Added on-failure and on-success destination settings for asynchronous invocation. Configure destinations to send an invocation record to an SNS topic, an SQS queue, an EventBridge event bus, or a Lambda function.Added error handling options to event source mappings. This enables you to configure the number of retries, configure the maximum age of records, or retry with smaller batches when an error occurs when a function processes a Kinesis or DynamoDB stream.Added the on-failure destination setting to event source mappings. This enables you to send discarded events to an SNS topic or SQS queue when all retries fail or when the maximum record age is exceeded when a function processes a Kinesis or DynamoDB stream.Added the ParallelizationFactor option to event source mappings to increase concurrency per shard when a function processes a Kinesis or DynamoDB stream." + }, + { + "type": "feature", + "category": "Amazon Data Lifecycle Manager", + "description": "You can now set time based retention policies on Data Lifecycle Manager. With this launch, DLM allows you to set snapshot retention period in the following interval units: days, weeks, months and years." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "AWS Systems Manager Documents now supports more Document Types: ApplicationConfiguration, ApplicationConfigurationSchema and DeploymentStrategy. This release also extends Document Permissions capabilities and introduces a new Force flag for DeleteDocument API." + }, + { + "type": "feature", + "category": "AWS Key Management Service", + "description": "AWS Key Management Service (KMS) now enables creation and use of asymmetric Customer Master Keys (CMKs) and the generation of asymmetric data key pairs." + }, + { + "type": "feature", + "category": "AWS IoT Secure Tunneling", + "description": "This release adds support for IoT Secure Tunneling to remote access devices behind restricted firewalls." + }, + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "Cluster Endpoints can now be tagged by using --tags in the create-db-cluster-endpoint API" + }, + { + "type": "feature", + "category": "Amazon Athena", + "description": "This release adds additional query lifecycle metrics to the QueryExecutionStatistics object in GetQueryExecution response." + }, + { + "type": "feature", + "category": "Amazon Simple Email Service", + "description": "This release includes support for automatically suppressing email addresses that result in hard bounce or complaint events at the account level, and for managing addresses on this account-level suppression list." + }, + { + "type": "feature", + "category": "Application Auto Scaling", + "description": "This release supports auto scaling of document classifier endpoints for Comprehend; and supports target tracking based on the average capacity utilization metric for AppStream 2.0 fleets." + }, + { + "type": "feature", + "category": "AWS WAFV2", + "description": "This release introduces new set of APIs (\"wafv2\") for AWS WAF. Major changes include single set of APIs for creating/updating resources in global and regional scope, and rules are configured directly into web ACL instead of being referenced. The previous APIs (\"waf\" and \"waf-regional\") are now referred as AWS WAF Classic. For more information visit: https://docs.aws.amazon.com/waf/latest/APIReference/Welcome.html" + }, + { + "type": "feature", + "category": "Amazon AppConfig", + "description": "Introducing AWS AppConfig, a new service that enables customers to quickly deploy validated configurations to applications of any size in a controlled and monitored fashion." + }, + { + "type": "feature", + "category": "Amazon Lex Runtime Service", + "description": "Amazon Lex adds \"sessionId\" attribute to the PostText and PostContent response." + }, + { + "type": "feature", + "category": "Amazon CloudWatch Application Insights", + "description": "CloudWatch Application Insights for .NET and SQL Server includes the follwing features: -Tagging Create and manage tags for your applications.-Custom log pattern matching. Define custom log patterns to be detected and monitored.-Resource-level permissions. Specify applications users can access." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds two new APIs: 1. ModifyDefaultCreditSpecification, which allows you to set default credit specification at the account level per AWS Region, per burstable performance instance family, so that all new burstable performance instances in the account launch using the new default credit specification. 2. GetDefaultCreditSpecification, which allows you to get current default credit specification per AWS Region, per burstable performance instance family. This release also adds new client exceptions for StartInstances and StopInstances." + }, + { + "type": "feature", + "category": "Amazon CloudWatch", + "description": "This release adds a new feature called \"Contributor Insights\". \"Contributor Insights\" supports the following 6 new APIs (PutInsightRule, DeleteInsightRules, EnableInsightRules, DisableInsightRules, DescribeInsightRules and GetInsightRuleReport)." + }, + { + "type": "feature", + "category": "Amazon Kinesis Analytics", + "description": "Kinesis Data Analytics service adds support to configure Java applications to access resources in a VPC. Also releasing support to configure Java applications to set allowNonRestoreState flag through the service APIs." + }, + { + "type": "feature", + "category": "Alexa For Business", + "description": "API update for Alexa for Business: This update enables the use of meeting room configuration that can be applied to a room profile. These settings help improve and measure utilization on Alexa for Business enabled rooms. New features include end meeting reminders, intelligent room release and room utilization analytics report." + }, + { + "type": "feature", + "category": "Amazon Redshift", + "description": "This release contains changes for 1. Redshift Scheduler 2. Update to the DescribeNodeConfigurationOptions to include a new action type recommend-node-config" + }, + { + "type": "feature", + "category": "AWS Greengrass", + "description": "IoT Greengrass supports machine learning resources in 'No container' mode." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.25.json b/.changes/2.10.25.json new file mode 100644 index 000000000000..e5890b50688f --- /dev/null +++ b/.changes/2.10.25.json @@ -0,0 +1,71 @@ +{ + "version": "2.10.25", + "date": "2019-11-26", + "entries": [ + { + "type": "feature", + "category": "AWS Directory Service", + "description": "This release will introduce optional encryption over LDAP network traffic using SSL certificates between customer's self-managed AD and AWS Directory Services instances. The release also provides APIs for Certificate management." + }, + { + "type": "feature", + "category": "AWS Organizations", + "description": "Introduces the DescribeEffectivePolicy action, which returns the contents of the policy that's in effect for the account." + }, + { + "type": "feature", + "category": "AWS RDS DataService", + "description": "Type hints to improve handling of some specific parameter types (date/time, decimal etc) for ExecuteStatement and BatchExecuteStatement APIs" + }, + { + "type": "feature", + "category": "AWS Resource Groups Tagging API", + "description": "You can use tag policies to help standardize on tags across your organization's resources." + }, + { + "type": "feature", + "category": "AWSServerlessApplicationRepository", + "description": "AWS Serverless Application Repository now supports verified authors. Verified means that AWS has made a good faith review, as a reasonable and prudent service provider, of the information provided by the requester and has confirmed that the requester's identity is as claimed." + }, + { + "type": "feature", + "category": "Amazon WorkSpaces", + "description": "For the WorkspaceBundle API, added the image identifier and the time of the last update." + }, + { + "type": "bugfix", + "category": "AWS Kinesis", + "description": "Reducing default read timeout and write timeout to 10 seconds for Kinesis client." + }, + { + "type": "feature", + "category": "Amazon Cognito Identity Provider", + "description": "This release adds a new setting for a user pool to configure which recovery methods a user can use to recover their account via the forgot password operation." + }, + { + "type": "feature", + "category": "Netty NIO HTTP Client", + "description": "Detect unhealthy http2 connections when read or write times out by sending PING frames" + }, + { + "type": "feature", + "category": "AWS MediaTailor", + "description": "AWS Elemental MediaTailor SDK now allows configuration of the Live Pre-Roll feature for HLS and DASH streams." + }, + { + "type": "feature", + "category": "Amazon DynamoDB", + "description": "1) Amazon Contributor Insights for Amazon DynamoDB is a diagnostic tool for identifying frequently accessed keys and understanding database traffic trends. 2) Support for displaying new fields when a table's encryption state is Inaccessible or the table have been Archived." + }, + { + "type": "feature", + "category": "Amazon Elastic Inference", + "description": "Amazon Elastic Inference allows customers to attach Elastic Inference Accelerators to Amazon EC2 and Amazon ECS tasks, thus providing low-cost GPU-powered acceleration and reducing the cost of running deep learning inference. This release allows customers to add or remove tags for their Elastic Inference Accelerators." + }, + { + "type": "feature", + "category": "Amazon QuickSight", + "description": "Documentation updates for QuickSight" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.26.json b/.changes/2.10.26.json new file mode 100644 index 000000000000..defe9cc2190b --- /dev/null +++ b/.changes/2.10.26.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.26", + "date": "2019-12-02", + "entries": [ + { + "type": "feature", + "category": "Schemas", + "description": "This release introduces support for Amazon EventBridge schema registry, making it easy to discover and write code for events in EventBridge." + }, + { + "type": "feature", + "category": "AWS License Manager", + "description": "AWS License Manager now automates discovery of bring-your-own-license usage across the customers organization. With few simple settings, customers can add bring your own license product information along with licensing rules, which would enable License Manager to automatically track the instances that have the specified products installed. If License Manager detects any violation of licensing rules, it would notify the customers designated license administrator to take corrective action." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Write operations (put, get, delete) now support 'conditionExpression'" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "AWS now provides a new BYOL experience for software licenses, such as Windows and SQL Server, that require a dedicated physical server. You can now enjoy the flexibility and cost effectiveness of using your own licenses on Amazon EC2 Dedicated Hosts, but with the simplicity, resiliency, and elasticity of AWS. You can specify your Dedicated Host management preferences, such as host allocation, host capacity utilization, and instance placement in AWS License Manager. Once set up, AWS takes care of these administrative tasks on your behalf, so that you can seamlessly launch virtual machines (instances) on Dedicated Hosts just like you would launch an EC2 instance with AWS provided licenses." + }, + { + "type": "feature", + "category": "EC2 Image Builder", + "description": "This is the first release of EC2 Image Builder, a service that provides a managed experience for automating the creation of EC2 AMIs." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.27.json b/.changes/2.10.27.json new file mode 100644 index 000000000000..735404abfa66 --- /dev/null +++ b/.changes/2.10.27.json @@ -0,0 +1,11 @@ +{ + "version": "2.10.27", + "date": "2019-12-02", + "entries": [ + { + "type": "feature", + "category": "Access Analyzer", + "description": "Introducing AWS IAM Access Analyzer, an IAM feature that makes it easy for AWS customers to ensure that their resource-based policies provide only the intended access to resources outside their AWS accounts." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.28.json b/.changes/2.10.28.json new file mode 100644 index 000000000000..7b8732bc8899 --- /dev/null +++ b/.changes/2.10.28.json @@ -0,0 +1,81 @@ +{ + "version": "2.10.28", + "date": "2019-12-03", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Introducing Amazon EKS with Fargate. Customers can now use Amazon EKS to launch pods directly onto AWS Fargate, the serverless compute engine built for containers on AWS." + }, + { + "type": "feature", + "category": "AWS Network Manager", + "description": "This is the initial SDK release for AWS Network Manager." + }, + { + "type": "feature", + "category": "Amazon Fraud Detector", + "description": "Amazon Fraud Detector is a fully managed service that makes it easy to identify potentially fraudulent online activities such as online payment fraud and the creation of fake accounts. Amazon Fraud Detector uses your data, machine learning (ML), and more than 20 years of fraud detection expertise from Amazon to automatically identify potentially fraudulent online activity so you can catch more fraud faster." + }, + { + "type": "feature", + "category": "AWS Compute Optimizer", + "description": "Initial release of AWS Compute Optimizer. AWS Compute Optimizer recommends optimal AWS Compute resources to reduce costs and improve performance for your workloads." + }, + { + "type": "feature", + "category": "Amazon Textract", + "description": "This SDK Release introduces Amazon Augmented AI support for Amazon Textract AnalyzeDocument API. Image byte payloads for synchronous operations have increased from 5 MB to 10 MB." + }, + { + "type": "feature", + "category": "AWS S3 Control", + "description": "Amazon S3 Access Points is a new S3 feature that simplifies managing data access at scale for shared data sets on Amazon S3. Access Points provide a customizable way to access the objects in a bucket, with a unique hostname and access policy that enforces the specific permissions and network controls for any request made through the access point. This represents a new way of provisioning access to shared data sets." + }, + { + "type": "feature", + "category": "Amazon EC2 Container Service", + "description": "This release supports ECS Capacity Providers, Fargate Spot, and ECS Cluster Auto Scaling. These features enable new ways for ECS to manage compute capacity used by tasks." + }, + { + "type": "feature", + "category": "AWSKendraFrontendService", + "description": "It is a preview launch of Amazon Kendra. Amazon Kendra is a managed, highly accurate and easy to use enterprise search service that is powered by machine learning." + }, + { + "type": "feature", + "category": "Amazon Elasticsearch Service", + "description": "UltraWarm storage provides a cost-effective way to store large amounts of read-only data on Amazon Elasticsearch Service. Rather than attached storage, UltraWarm nodes use Amazon S3 and a sophisticated caching solution to improve performance. For indices that you are not actively writing to and query less frequently, UltraWarm storage offers significantly lower costs per GiB. In Elasticsearch, these warm indices behave just like any other index. You can query them using the same APIs or use them to create dashboards in Kibana." + }, + { + "type": "feature", + "category": "AWS Outposts", + "description": "This is the initial release for AWS Outposts, a fully managed service that extends AWS infrastructure, services, APIs, and tools to customer sites. AWS Outposts enables you to launch and run EC2 instances and EBS volumes locally at your on-premises location. This release introduces new APIs for creating and viewing Outposts." + }, + { + "type": "feature", + "category": "Amazon Simple Storage Service", + "description": "Amazon S3 Access Points is a new S3 feature that simplifies managing data access at scale for shared data sets on Amazon S3. Access Points provide a customizable way to access the objects in a bucket, with a unique hostname and access policy that enforces the specific permissions and network controls for any request made through the access point. This represents a new way of provisioning access to shared data sets." + }, + { + "type": "feature", + "category": "Amazon CodeGuru Profiler", + "description": "(New Service) Amazon CodeGuru Profiler analyzes application CPU utilization and latency characteristics to show you where you are spending the most cycles in your application. This analysis is presented in an interactive flame graph that helps you easily understand which paths consume the most resources, verify that your application is performing as expected, and uncover areas that can be optimized further." + }, + { + "type": "feature", + "category": "Amazon Augmented AI Runtime", + "description": "This release adds support for Amazon Augmented AI, which makes it easy to build workflows for human review of machine learning predictions." + }, + { + "type": "feature", + "category": "Amazon CodeGuru Reviewer", + "description": "This is the preview release of Amazon CodeGuru Reviewer." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds support for the following features: 1. An option to enable acceleration for Site-to-Site VPN connections; 2. Inf1 instances featuring up to 16 AWS Inferentia chips; 3. The ability to associate route tables with internet gateways and virtual private gateways; 4. AWS Local Zones that place compute, storage, database, and other select services; 5. Launching and viewing EC2 instances and EBS volumes running locally in Outposts; 6. Peering Transit Gateways between regions simplifying creation of secure and private global networks on AWS; 7. Transit Gateway Multicast, enabling multicast routing within and between VPCs using Transit Gateway as a multicast router." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.29.json b/.changes/2.10.29.json new file mode 100644 index 000000000000..2be5a2908220 --- /dev/null +++ b/.changes/2.10.29.json @@ -0,0 +1,46 @@ +{ + "version": "2.10.29", + "date": "2019-12-03", + "entries": [ + { + "type": "feature", + "category": "AWS Step Functions", + "description": "This release of the AWS Step Functions SDK introduces support for Express Workflows." + }, + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "This release adds support for the Amazon RDS Proxy" + }, + { + "type": "feature", + "category": "Amazon Rekognition", + "description": "This SDK Release introduces APIs for Amazon Rekognition Custom Labels feature (CreateProjects, CreateProjectVersion,DescribeProjects, DescribeProjectVersions, StartProjectVersion, StopProjectVersion and DetectCustomLabels). Also new is AugmentedAI (Human In The Loop) Support for DetectModerationLabels in Amazon Rekognition." + }, + { + "type": "feature", + "category": "Amazon SageMaker Service", + "description": "You can now use SageMaker Autopilot for automatically training and tuning candidate models using a combination of various feature engineering, ML algorithms, and hyperparameters determined from the user's input data. SageMaker Automatic Model Tuning now supports tuning across multiple algorithms. With Amazon SageMaker Experiments users can create Experiments, ExperimentTrials, and ExperimentTrialComponents to track, organize, and evaluate their ML training jobs. With Amazon SageMaker Debugger, users can easily debug training jobs using a number of pre-built rules provided by Amazon SageMaker, or build custom rules. With Amazon SageMaker Processing, users can run on-demand, distributed, and fully managed jobs for data pre- or post- processing or model evaluation. With Amazon SageMaker Model Monitor, a user can create MonitoringSchedules to automatically monitor endpoints to detect data drift and other issues and get alerted on them. This release also includes the preview version of Amazon SageMaker Studio with Domains, UserProfiles, and Apps. This release also includes the preview version of Amazon Augmented AI to easily implement human review of machine learning predictions by creating FlowDefinitions, HumanTaskUis, and HumanLoops." + }, + { + "type": "feature", + "category": "AWS Lambda", + "description": "- Added the ProvisionedConcurrency type and operations. Allocate provisioned concurrency to enable your function to scale up without fluctuations in latency. Use PutProvisionedConcurrencyConfig to configure provisioned concurrency on a version of a function, or on an alias." + }, + { + "type": "feature", + "category": "Application Auto Scaling", + "description": "This release supports auto scaling of provisioned concurrency for AWS Lambda." + }, + { + "type": "feature", + "category": "Amazon Elastic Block Store", + "description": "This release introduces the EBS direct APIs for Snapshots: 1. ListSnapshotBlocks, which lists the block indexes and block tokens for blocks in an Amazon EBS snapshot. 2. ListChangedBlocks, which lists the block indexes and block tokens for blocks that are different between two snapshots of the same volume/snapshot lineage. 3. GetSnapshotBlock, which returns the data in a block of an Amazon EBS snapshot." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Interacting with an access point in a different region to the one the S3 client is configured for will no longer result in the request being signed for the wrong region and rejected by S3." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.30.json b/.changes/2.10.30.json new file mode 100644 index 000000000000..72427a8b0f5e --- /dev/null +++ b/.changes/2.10.30.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.30", + "date": "2019-12-04", + "entries": [ + { + "type": "feature", + "category": "AmazonApiGatewayV2", + "description": "Amazon API Gateway now supports HTTP APIs (beta), enabling customers to quickly build high performance RESTful APIs that are up to 71% cheaper than REST APIs also available from API Gateway. HTTP APIs are optimized for building APIs that proxy to AWS Lambda functions or HTTP backends, making them ideal for serverless workloads. Using HTTP APIs, you can secure your APIs using OIDC and OAuth 2 out of box, quickly build web applications using a simple CORS experience, and get started immediately with automatic deployment and simple create workflows." + }, + { + "type": "feature", + "category": "Amazon Kinesis Video Signaling Channels", + "description": "Announcing support for WebRTC in Kinesis Video Streams, as fully managed capability. You can now use simple APIs to enable your connected devices, web, and mobile apps with real-time two-way media streaming capabilities." + }, + { + "type": "feature", + "category": "Amazon Kinesis Video Streams", + "description": "Introduces management of signaling channels for Kinesis Video Streams." + }, + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Fixed an issue where receiving a GOAWAY that would cause the closing of all streams could cause all outstanding streams to be completed successfully instead of exceptionally." + }, + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Fixed an issue where closing the last stream on a connection that had been closed or received a GOAWAY did not close the connection." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.31.json b/.changes/2.10.31.json new file mode 100644 index 000000000000..f2a27b9ed723 --- /dev/null +++ b/.changes/2.10.31.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.31", + "date": "2019-12-09", + "entries": [ + { + "type": "feature", + "category": "Managed Streaming for Kafka", + "description": "AWS MSK has added support for Open Monitoring with Prometheus." + }, + { + "type": "feature", + "category": "AWS Key Management Service", + "description": "The Verify operation now returns KMSInvalidSignatureException on invalid signatures. The Sign and Verify operations now return KMSInvalidStateException when a request is made against a CMK pending deletion." + }, + { + "type": "feature", + "category": "Netty NIO HTTP Client", + "description": "Close HTTP/2 connections if they have had 0 streams for 5 seconds. This can be disabled using `useIdleConnectionReaper(false)` or have the time period adjusted using `connectionMaxIdleTime(...)` on the `NettyNioAsyncHttpClient.Builder`." + }, + { + "type": "feature", + "category": "Netty NIO HTTP Client", + "description": "Periodically ping HTTP/2 connections and close them if the service does not respond. The ping periodicity and timeout time is not currently configurable." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "Adds the SSM GetCalendarState API and ChangeCalendar SSM Document type. These features enable the forthcoming Systems Manager Change Calendar feature, which will allow you to schedule events during which actions should (or should not) be performed." + }, + { + "type": "feature", + "category": "Amazon QuickSight", + "description": "Documentation updates for QuickSight" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.32.json b/.changes/2.10.32.json new file mode 100644 index 000000000000..937ef7cab10f --- /dev/null +++ b/.changes/2.10.32.json @@ -0,0 +1,11 @@ +{ + "version": "2.10.32", + "date": "2019-12-10", + "entries": [ + { + "type": "feature", + "category": "AWSKendraFrontendService", + "description": "1. Adding DocumentTitleFieldName as an optional configuration for SharePoint. 2. updating s3 object pattern to support all s3 keys." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.33.json b/.changes/2.10.33.json new file mode 100644 index 000000000000..d52799f34216 --- /dev/null +++ b/.changes/2.10.33.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.33", + "date": "2019-12-11", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release allows customers to attach multiple Elastic Inference Accelerators to a single EC2 instance. It adds support for a Count parameter for each Elastic Inference Accelerator type you specify on the RunInstances and LaunchTemplate APIs." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Adds a `has*` method to requests and responses that have a List or Map property." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.34.json b/.changes/2.10.34.json new file mode 100644 index 000000000000..240cb63b85cf --- /dev/null +++ b/.changes/2.10.34.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.34", + "date": "2019-12-12", + "entries": [ + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Fixing exception using `RequestBody.fromInputStream` on non-resettable `InputStreams` by making `reset` conditional on `markSupported`. See [#1544](https://github.com/aws/aws-sdk-java-v2/issues/1544) / [#1545](https://github.com/aws/aws-sdk-java-v2/issues/1545)" + }, + { + "type": "feature", + "category": "Access Analyzer", + "description": "This release includes improvements and fixes bugs for the IAM Access Analyzer feature." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.35.json b/.changes/2.10.35.json new file mode 100644 index 000000000000..2b511b0bb05e --- /dev/null +++ b/.changes/2.10.35.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.35", + "date": "2019-12-13", + "entries": [ + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "CodeBuild adds support for cross account" + }, + { + "type": "feature", + "category": "Amazon Simple Email Service", + "description": "Added the ability to use your own public-private key pair to configure DKIM authentication for a domain identity." + }, + { + "type": "feature", + "category": "Amazon Detective", + "description": "This is the initial release of Amazon Detective." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.36.json b/.changes/2.10.36.json new file mode 100644 index 000000000000..81f1306f41f6 --- /dev/null +++ b/.changes/2.10.36.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.36", + "date": "2019-12-16", + "entries": [ + { + "type": "feature", + "category": "AmazonMQ", + "description": "Amazon MQ now supports throughput-optimized message brokers, backed by Amazon EBS." + }, + { + "type": "feature", + "category": "AWS Comprehend Medical", + "description": "New Ontology linking APIs will provides medication concepts normalization and Diagnoses codes from input text. In this release we will provide two APIs - RxNorm and ICD10-CM." + }, + { + "type": "feature", + "category": "Amazon S3", + "description": "CopyObjectRequest now has `destinationBucket` and `destinationKey` properties for clarity.\nThe existing names, `bucket` and `key`, are deprecated." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "You can now configure your EC2 Fleet to preferentially use EC2 Capacity Reservations for launching On-Demand instances, enabling you to fully utilize the available (and unused) Capacity Reservations before launching On-Demand instances on net new capacity." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.37.json b/.changes/2.10.37.json new file mode 100644 index 000000000000..108eb477483d --- /dev/null +++ b/.changes/2.10.37.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.37", + "date": "2019-12-17", + "entries": [ + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "Added support for Cloud Watch Output and Document Version to the Run Command tasks in Maintenance Windows." + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "Added a new Over-the-Air (OTA) Update feature that allows you to use different, or multiple, protocols to transfer an image from the AWS cloud to IoT devices." + }, + { + "type": "feature", + "category": "Amazon Kinesis Analytics", + "description": "Kinesis Data Analytics service now supports running Java applications using Flink 1.8." + }, + { + "type": "feature", + "category": "AWS Elemental MediaLive", + "description": "AWS Elemental MediaLive now supports HLS ID3 segment tagging, HLS redundant manifests for CDNs that support different publishing/viewing endpoints, fragmented MP4 (fMP4), and frame capture intervals specified in milliseconds." + }, + { + "type": "feature", + "category": "Amazon EC2 Container Service", + "description": "Documentation updates for Amazon ECS." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Documentation updates for Amazon EC2" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.38.json b/.changes/2.10.38.json new file mode 100644 index 000000000000..56d491584d3e --- /dev/null +++ b/.changes/2.10.38.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.38", + "date": "2019-12-18", + "entries": [ + { + "type": "feature", + "category": "Amazon Simple Storage Service", + "description": "Updates Amazon S3 endpoints allowing you to configure your client to opt-in to using S3 with the us-east-1 regional endpoint, instead of global." + }, + { + "type": "feature", + "category": "AWS Resource Groups Tagging API", + "description": "Documentation updates for resourcegroupstaggingapi" + }, + { + "type": "feature", + "category": "AWS OpsWorks CM", + "description": "AWS OpsWorks CM now supports tagging, and tag-based access control, of servers and backups." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release introduces the ability to tag Elastic Graphics accelerators. You can use tags to organize and identify your accelerators for cost allocation." + }, + { + "type": "feature", + "category": "Amazon CloudFront", + "description": "Documentation updates for CloudFront" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.39.json b/.changes/2.10.39.json new file mode 100644 index 000000000000..ea5f3af373e6 --- /dev/null +++ b/.changes/2.10.39.json @@ -0,0 +1,56 @@ +{ + "version": "2.10.39", + "date": "2019-12-19", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "We are updating the supportedRootDevices field to supportedRootDeviceTypes for DescribeInstanceTypes API to ensure that the actual value is returned, correcting a previous error in the model." + }, + { + "type": "feature", + "category": "Amazon Data Lifecycle Manager", + "description": "You can now copy snapshots across regions using Data Lifecycle Manager (DLM). You can enable policies which, along with create, can now also copy snapshots to one or more AWS region(s). Copies can be scheduled for up to three regions from a single policy and retention periods are set for each region separately." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "This release allows customers to add tags to Automation execution, enabling them to sort and filter executions in different ways, such as by resource, purpose, owner, or environment." + }, + { + "type": "feature", + "category": "Amazon Personalize Runtime", + "description": "Add context map to get-recommendations and get-personalized-ranking request objects to provide contextual metadata at inference time" + }, + { + "type": "feature", + "category": "Amazon GameLift", + "description": "Amazon GameLift now supports ARNs for all key GameLift resources, tagging for GameLift resource authorization management, and updated documentation that articulates GameLift's resource authorization strategy." + }, + { + "type": "feature", + "category": "Amazon Lex Model Building Service", + "description": "Amazon Lex now supports conversation logs and slot obfuscation." + }, + { + "type": "feature", + "category": "Netty NIO HTTP Client", + "description": "`SETTINGS_INITIAL_WINDOW_SIZE` is now configurable on HTTP/2 connections opened by the Netty client using `Http2Configuration#initialWindowSize(Integer)` along with `NettyNioAsyncHttpClient.Builder#http2Configuration(Http2Configuration)`. See https://tools.ietf.org/html/rfc7540#section-6.5.2 for more information." + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "Amazon Transcribe supports job queuing for the StartTranscriptionJob API." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Fixed an issue where a 'checksum mismatch' error is raised whenever a PutObject request is retried while using an async client." + }, + { + "type": "feature", + "category": "AWS CodeStar connections", + "description": "Public beta for Bitbucket Cloud support in AWS CodePipeline through integration with AWS CodeStar connections." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.40.json b/.changes/2.10.40.json new file mode 100644 index 000000000000..27e005b45fca --- /dev/null +++ b/.changes/2.10.40.json @@ -0,0 +1,56 @@ +{ + "version": "2.10.40", + "date": "2019-12-20", + "entries": [ + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "This release adds an operation that enables users to specify whether a database is restarted when its SSL/TLS certificate is rotated. Only customers who do not use SSL/TLS should use this operation." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Fixed an issue where the SDK would attempt to validate the checksum on a PutObjectRequest when S3 was returning invalid checksums. This would cause all requests to buckets with customer-managed-key service-side encryption to fail." + }, + { + "type": "feature", + "category": "AWS Device Farm", + "description": "Introduced browser testing support through AWS Device Farm" + }, + { + "type": "feature", + "category": "Amazon Redshift", + "description": "Documentation updates for Amazon Redshift RA3 node types." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "Additional resource types are now fully supported in the AWS Security Finding Format (ASFF). These resources include AwsElbv2LoadBalancer, AwsKmsKey, AwsIamRole, AwsSqsQueue, AwsLambdaFunction, AwsSnsTopic, and AwsCloudFrontDistribution. Each of these resource types includes an accompanying resource details object with fields for security finding providers to populate. Updates were made to the AwsIamAccessKey resource details object to include information on principal ID and name. To learn more, visit our documentation on the ASFF." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release introduces the ability to tag key pairs, placement groups, export tasks, import image tasks, import snapshot tasks and export image tasks. You can use tags to organize and identify your resources for cost allocation." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "This release updates the attachments support to include AttachmentReference source for Automation documents." + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "AWS Transcribe now supports vocabulary filtering that allows customers to input words to the service that they don't want to see in the output transcript." + }, + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Amazon EKS now supports restricting access to the API server public endpoint by applying CIDR blocks" + }, + { + "type": "feature", + "category": "Amazon Pinpoint", + "description": "This release of the Amazon Pinpoint API introduces versioning support for message templates." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.41.json b/.changes/2.10.41.json new file mode 100644 index 000000000000..1236ebe1d24b --- /dev/null +++ b/.changes/2.10.41.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.41", + "date": "2019-12-23", + "entries": [ + { + "type": "feature", + "category": "Amazon Detective", + "description": "Updated the documentation for Amazon Detective." + }, + { + "type": "feature", + "category": "Amazon FSx", + "description": "This release adds a new family of APIs (create-data-repository-task, describe-data-repository-task, and cancel-data-repository-task) that allow users to perform operations between their file system and its linked data repository." + }, + { + "type": "feature", + "category": "AWS Health APIs and Notifications", + "description": "With this release, you can now centrally aggregate AWS Health events from all accounts in your AWS organization. Visit AWS Health documentation to learn more about enabling and using this feature: https://docs.aws.amazon.com/health/latest/ug/organizational-view-health.html." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.42.json b/.changes/2.10.42.json new file mode 100644 index 000000000000..f248a856c652 --- /dev/null +++ b/.changes/2.10.42.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.42", + "date": "2020-01-02", + "entries": [ + { + "type": "feature", + "category": "Amazon Lightsail", + "description": "This release adds support for Certificate Authority (CA) certificate identifier to managed databases in Amazon Lightsail." + }, + { + "type": "feature", + "category": "Amazon EC2 Container Registry", + "description": "Adds waiters for ImageScanComplete and LifecyclePolicyPreviewComplete" + }, + { + "type": "feature", + "category": "Amazon Lex Model Building Service", + "description": "Documentation updates for Amazon Lex." + }, + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "Documentation updates for GetReservationUtilization for the Cost Explorer API." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Fix unmarshalling for models with xml attributes. See [#1488](https://github.com/aws/aws-sdk-java-v2/issues/1488)." + }, + { + "type": "bugfix", + "category": "Netty NIO Http Client", + "description": "Propagate exception properly when an exception is thrown from protocol initialization." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.43.json b/.changes/2.10.43.json new file mode 100644 index 000000000000..5babc4d8bb6d --- /dev/null +++ b/.changes/2.10.43.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.43", + "date": "2020-01-06", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Add `RequestBody.fromRemainingByteBuffer(ByteBuffer)` that copies only the remaining readable bytes of the buffer. See [#1534](https://github.com/aws/aws-sdk-java-v2/issues/1534)" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release supports service providers configuring a private DNS name for services other than AWS services and services available in the AWS marketplace. This feature allows consumers to access the service using an existing DNS name without making changes to their applications." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Reduce ReadTimeout and ConnectTimeout for accessing EC2 metadata instance service" + }, + { + "type": "feature", + "category": "AWS Elemental MediaPackage", + "description": "You can now restrict direct access to AWS Elemental MediaPackage by securing requests for live content using CDN authorization. With CDN authorization, content requests require a specific HTTP header and authorization code." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Requests that return an error response in the body of the HTTP response with a successful (200) status code will now correctly be handled as a failed request by the SDK." + }, + { + "type": "feature", + "category": "Amazon Comprehend", + "description": "Amazon Comprehend now supports Multilabel document classification" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.44.json b/.changes/2.10.44.json new file mode 100644 index 000000000000..a20340f5a968 --- /dev/null +++ b/.changes/2.10.44.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.44", + "date": "2020-01-07", + "entries": [ + { + "type": "feature", + "category": "AWS X-Ray", + "description": "Documentation updates for xray" + }, + { + "type": "feature", + "category": "AWS Migration Hub", + "description": "ListApplicationStates API provides a list of all application migration states" + }, + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "Add encryption key override to StartBuild API in AWS CodeBuild." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.45.json b/.changes/2.10.45.json new file mode 100644 index 000000000000..345285efb682 --- /dev/null +++ b/.changes/2.10.45.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.45", + "date": "2020-01-08", + "entries": [ + { + "type": "feature", + "category": "Amazon Translate", + "description": "This release adds a new family of APIs for asynchronous batch translation service that provides option to translate large collection of text or HTML documents stored in Amazon S3 folder. This service accepts a batch of up to 5 GB in size per API call with each document not exceeding 1 MB size and the number of documents not exceeding 1 million per batch. See documentation for more information." + }, + { + "type": "feature", + "category": "Firewall Management Service", + "description": "AWS Firewall Manager now supports tagging, and tag-based access control, of policies." + }, + { + "type": "feature", + "category": "AWS Step Functions", + "description": "Add sfn specific http configurations. See [#1325](https://github.com/aws/aws-sdk-java-v2/issues/1325)" + }, + { + "type": "bugfix", + "category": "Amazon EC2", + "description": "Fix NPE when calling `CopySnapshot`. Fixes [#1564](https://github.com/aws/aws-sdk-java-v2/issues/1564)" + }, + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "Documentation updates for CreateCostCategoryDefinition and UpdateCostCategoryDefinition API" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.46.json b/.changes/2.10.46.json new file mode 100644 index 000000000000..fa6f8c9dad67 --- /dev/null +++ b/.changes/2.10.46.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.46", + "date": "2020-01-09", + "entries": [ + { + "type": "feature", + "category": "Amazon CloudWatch Logs", + "description": "Documentation updates for logs" + }, + { + "type": "feature", + "category": "AWS Security Token Service", + "description": "Documentation updates for sts" + }, + { + "type": "feature", + "category": "Amazon S3", + "description": "Add support for Tagging builder in `CreateMultipartUploadRequest`. See [#1440](https://github.com/aws/aws-sdk-java-v2/issues/1440)" + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Increase the priority of the AWS_WEB_IDENTITY_TOKEN_FILE/AWS_ROLE_ARN/AWS_ROLE_SESSION_NAME environment variables when loading credentials so that they are considered before web_identity_token_file/role_arn/role_session_name profile properties. This is consistent with the other AWS SDKs, including the CLI." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.47.json b/.changes/2.10.47.json new file mode 100644 index 000000000000..67a82e693156 --- /dev/null +++ b/.changes/2.10.47.json @@ -0,0 +1,51 @@ +{ + "version": "2.10.47", + "date": "2020-01-10", + "entries": [ + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "This release adds an operation that enables users to override the system-default SSL/TLS certificate for new Amazon RDS DB instances temporarily, or remove the customer override." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release introduces the ability to tag egress only internet gateways, local gateways, local gateway route tables, local gateway virtual interfaces, local gateway virtual interface groups, local gateway route table VPC association and local gateway route table virtual interface group association. You can use tags to organize and identify your resources for cost allocation." + }, + { + "type": "feature", + "category": "Amazon WorkSpaces", + "description": "Added the migrate feature to Amazon WorkSpaces." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon Chime", + "description": "Add shared profile support to new and existing users" + }, + { + "type": "feature", + "category": "AWS Transfer for SFTP", + "description": "This release introduces a new endpoint type that allows you to attach Elastic IP addresses from your AWS account with your server's endpoint directly and whitelist access to your server by client's internet IP address(es) using VPC Security Groups." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoints and added global endpoints for iso and iso-b." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Fix an issue where s3#listObjects incorrectly decoded marker field. See [#1574](https://github.com/aws/aws-sdk-java-v2/issues/1574)." + }, + { + "type": "feature", + "category": "Amazon SageMaker Service", + "description": "SageMaker ListTrialComponents API filter by TrialName and ExperimentName." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.48.json b/.changes/2.10.48.json new file mode 100644 index 000000000000..f1c00df53d78 --- /dev/null +++ b/.changes/2.10.48.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.48", + "date": "2020-01-13", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Documentation updates for the StopInstances API. You can now stop and start an Amazon EBS-backed Spot Instance at will, instead of relying on the Stop interruption behavior to stop your Spot Instances when interrupted." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Fixed bug prevent GetBucketBolicy from ever being successful using the asynchronous S3 client." + }, + { + "type": "feature", + "category": "AWS Backup", + "description": "Cross-region backup is a new AWS Backup feature that allows enterprises to copy backups across multiple AWS services to different regions." + }, + { + "type": "feature", + "category": "Amazon Elastic File System", + "description": "This release adds support for managing EFS file system policies and EFS Access Points." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.49.json b/.changes/2.10.49.json new file mode 100644 index 000000000000..2964ff467b6d --- /dev/null +++ b/.changes/2.10.49.json @@ -0,0 +1,11 @@ +{ + "version": "2.10.49", + "date": "2020-01-14", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds support for partition placement groups and instance metadata option in Launch Templates" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.50.json b/.changes/2.10.50.json new file mode 100644 index 000000000000..29de159bf5d0 --- /dev/null +++ b/.changes/2.10.50.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.50", + "date": "2020-01-15", + "entries": [ + { + "type": "bugfix", + "category": "Amazon Transcribe Service", + "description": "Fixed an issue where streaming transcriptions would fail with signature validation errors if the date changed during the request." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "Document updates for Patch Manager 'NoReboot' feature." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "General Update to EC2 Docs and SDKs" + }, + { + "type": "feature", + "category": "AWS Organizations", + "description": "Updated description for PolicyID parameter and ConstraintViolationException." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "Add support for DescribeStandardsControls and UpdateStandardsControl. These new Security Hub API operations are used to track and manage whether a compliance standards control is enabled." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.51.json b/.changes/2.10.51.json new file mode 100644 index 000000000000..a11e9ee1cfc1 --- /dev/null +++ b/.changes/2.10.51.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.51", + "date": "2020-01-16", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Client VPN now supports Port Configuration for VPN Endpoints, allowing usage of either port 443 or port 1194." + }, + { + "type": "feature", + "category": "AWS Directory Service", + "description": "To reduce the number of errors our customers are facing, we have modified the requirements of input parameters for two of Directory Service APIs." + }, + { + "type": "feature", + "category": "Amazon SageMaker Service", + "description": "This release adds two new APIs (UpdateWorkforce and DescribeWorkforce) to SageMaker Ground Truth service for workforce IP whitelisting." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.52.json b/.changes/2.10.52.json new file mode 100644 index 000000000000..70463cd52e87 --- /dev/null +++ b/.changes/2.10.52.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.52", + "date": "2020-01-17", + "entries": [ + { + "type": "feature", + "category": "AWS CloudHSM V2", + "description": "This release introduces resource-level and tag-based access control for AWS CloudHSM resources. You can now tag CloudHSM backups, tag CloudHSM clusters on creation, and tag a backup as you copy it to another region." + }, + { + "type": "feature", + "category": "Amazon Neptune", + "description": "This release includes Deletion Protection for Amazon Neptune databases." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon EC2 Container Service", + "description": "This release provides a public preview for specifying Amazon EFS file systems as volumes in your Amazon ECS task definitions." + }, + { + "type": "feature", + "category": "Amazon Redshift", + "description": "Documentation updates for redshift" + }, + { + "type": "feature", + "category": "AWS Elemental MediaConvert", + "description": "AWS Elemental MediaConvert SDK has added support for MP3 audio only outputs." + }, + { + "type": "feature", + "category": "AWS Batch", + "description": "This release ensures INACTIVE job definitions are permanently deleted after 180 days." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.53.json b/.changes/2.10.53.json new file mode 100644 index 000000000000..91314cfc854f --- /dev/null +++ b/.changes/2.10.53.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.53", + "date": "2020-01-20", + "entries": [ + { + "type": "feature", + "category": "AWS Key Management Service", + "description": "The ConnectCustomKeyStore operation now provides new error codes (USER_LOGGED_IN and USER_NOT_FOUND) for customers to better troubleshoot if their connect custom key store operation fails. Password length validation during CreateCustomKeyStore now also occurs on the client side." + }, + { + "type": "feature", + "category": "Amazon CloudWatch", + "description": "Updating DescribeAnomalyDetectors API to return AnomalyDetector Status value in response." + }, + { + "type": "feature", + "category": "Alexa For Business", + "description": "Add support for CreatedTime and ConnectionStatusUpdatedTime in response of SearchDevices API." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release provides support for a preview of bringing your own IPv6 addresses (BYOIP for IPv6) for use in AWS." + }, + { + "type": "feature", + "category": "Amazon CloudWatch Application Insights", + "description": "This release adds support for a list API to retrieve the configuration events logged during periodic updates to an application by Amazon CloudWatch Application Insights." + }, + { + "type": "feature", + "category": "AWS Lambda", + "description": "Added reason codes to StateReasonCode (InvalidSubnet, InvalidSecurityGroup) and LastUpdateStatusReasonCode (SubnetOutOfIPAddresses, InvalidSubnet, InvalidSecurityGroup) for functions that connect to a VPC." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.54.json b/.changes/2.10.54.json new file mode 100644 index 000000000000..5a4d34a1dbcc --- /dev/null +++ b/.changes/2.10.54.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.54", + "date": "2020-01-21", + "entries": [ + { + "type": "feature", + "category": "AWS CodePipeline", + "description": "AWS CodePipeline enables an ability to stop pipeline executions." + }, + { + "type": "feature", + "category": "AWS Application Discovery Service", + "description": "Documentation updates for the AWS Application Discovery Service." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS IoT Events", + "description": "Documentation updates for iotcolumbo" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Add an enum value to the result of DescribeByoipCidrs to support CIDRs that are not publicly advertisable." + }, + { + "type": "bugfix", + "category": "Netty NIO Http Client", + "description": "Fixed a bug where an inactive http2 connection without `GOAWAY` frame received might get reused in a new request, causing `ClosedChannelException`" + }, + { + "type": "feature", + "category": "AWS Marketplace Commerce Analytics", + "description": "Remove 4 deprecated data sets, change some data sets available dates to 2017-09-15" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.55.json b/.changes/2.10.55.json new file mode 100644 index 000000000000..27994f2cf066 --- /dev/null +++ b/.changes/2.10.55.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.55", + "date": "2020-01-23", + "entries": [ + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "This SDK release introduces APIs that automate the export of Amazon RDS snapshot data to Amazon S3. The new APIs include: StartExportTask, CancelExportTask, DescribeExportTasks. These APIs automate the extraction of data from an RDS snapshot and export it to an Amazon S3 bucket. The data is stored in a compressed, consistent, and query-able format. After the data is exported, you can query it directly using tools such as Amazon Athena or Redshift Spectrum. You can also consume the data as part of a data lake solution. If you archive the data in S3 Infrequent Access or Glacier, you can reduce long term data storage costs by applying data lifecycle policies." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Added ServiceMetadata.servicePartitions() to get partition metadata for a specific service" + }, + { + "type": "feature", + "category": "AWS Identity and Access Management", + "description": "This release enables the Identity and Access Management policy simulator to simulate permissions boundary policies." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Support for non-blocking asynchronous calling of all mapper operations" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Improved error messages on UnknownHostExceptions" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.56.json b/.changes/2.10.56.json new file mode 100644 index 000000000000..dab2bee26028 --- /dev/null +++ b/.changes/2.10.56.json @@ -0,0 +1,46 @@ +{ + "version": "2.10.56", + "date": "2020-01-24", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Adding new error codes for Nodegroups in EKS" + }, + { + "type": "feature", + "category": "Amazon EC2", + "description": "Adds EC2ThrottledException as a recognized throttling exception to be retried" + }, + { + "type": "feature", + "category": "AWS OpsWorks CM", + "description": "AWS OpsWorks for Chef Automate now supports in-place upgrade to Chef Automate 2. Eligible servers can be updated through the management console, CLI and APIs." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon WorkSpaces", + "description": "Documentation updates for WorkSpaces" + }, + { + "type": "feature", + "category": "Amazon EC2 Container Service", + "description": "This release provides support for tagging Amazon ECS task sets for services using external deployment controllers." + }, + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Fix issue where DNS resolution for a host is only made once for the initial request to the host. If the DNS entries change for a hostname, the client will resolve the new address until the client is closed and recreated." + }, + { + "type": "feature", + "category": "AWS DataSync", + "description": "AWS DataSync now supports FSx for Windows File Server Locations" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.57.json b/.changes/2.10.57.json new file mode 100644 index 000000000000..6de1d762eff8 --- /dev/null +++ b/.changes/2.10.57.json @@ -0,0 +1,71 @@ +{ + "version": "2.10.57", + "date": "2020-02-04", + "entries": [ + { + "type": "feature", + "category": "AWS Storage Gateway", + "description": "Adding KVM as a support hypervisor" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Deliver exceptions to stream channels correctly if there's an exception thrown on connection. This also fixes a bug where publisher signals onComplete if the stream is closed as a result of outbound GOAWAY." + }, + { + "type": "feature", + "category": "Amazon WorkMail", + "description": "This release adds support for tagging Amazon WorkMail organizations." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Fixed an issue where fields in `ListObjectVersionsResponse` and `ListMultipartUploadsResponse` are not decoded correctly when encodingType is specified as url. See [#1601](https://github.com/aws/aws-sdk-java-v2/issues/1601)" + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "Updated ThrottlingException documentation to report that the error code is 400, and not 429, to reflect actual system behaviour." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Changing usage of typed builders for PutItem, UpdateItem and StaticTableSchema to explicitly provide class type." + }, + { + "type": "feature", + "category": "Managed Streaming for Kafka", + "description": "This release enables AWS MSK customers to list Apache Kafka versions that are supported on AWS MSK clusters. Also includes changes to expose additional details of a cluster's state in DescribeCluster and ListClusters APIs." + }, + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Throws `IOException` for the race condition where an HTTP2 connection gets reused at the same time it gets inactive so that failed requests can be retried" + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Renames top level sync/async MappedDatabase interfaces as DynamoDbEnhancedClient interfaces. Also adds builder definitions to the interfaces together with a static method that returns the default implementation of the builder." + }, + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "This feature ensures that an instance is patched up to the available patches on a particular date. It can be enabled by selecting the 'ApproveUntilDate' option as the auto-approval rule while creating the patch baseline. ApproveUntilDate - The cutoff date for auto approval of released patches. Any patches released on or before this date will be installed automatically." + }, + { + "type": "feature", + "category": "Amazon CloudFront", + "description": "Documentation updates for CloudFront" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Amazon VPC Flow Logs adds support for 1-minute aggregation intervals." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.58.json b/.changes/2.10.58.json new file mode 100644 index 000000000000..52ddb0cc73b8 --- /dev/null +++ b/.changes/2.10.58.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.58", + "date": "2020-02-05", + "entries": [ + { + "type": "feature", + "category": "Amazon Data Lifecycle Manager", + "description": "Updated the maximum number of tags that can be added to a snapshot using DLM to 45." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release provides support for tagging when you create a VPC endpoint, or VPC endpoint service." + }, + { + "type": "feature", + "category": "AWS Ground Station", + "description": "Adds dataflowEndpointRegion property to DataflowEndpointConfig. The dateCreated, lastUpdated, and tags properties on GetSatellite have been deprecated." + }, + { + "type": "feature", + "category": "AWS Elemental MediaConvert", + "description": "AWS Elemental MediaConvert SDK has added support for fine-tuned QVBR quality level." + }, + { + "type": "feature", + "category": "AWS Resource Groups Tagging API", + "description": "Documentation-only update that adds services to the list of supported services." + }, + { + "type": "feature", + "category": "Amazon Forecast Query Service", + "description": "Documentation updates for Amazon Forecast." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "Additional resource types are now supported in the AWS Security Finding Format (ASFF). The following new resource types are added, each having an accompanying resource details object with fields for security finding providers to populate: AwsCodeBuildProject, AwsEc2NetworkInterface, AwsEc2SecurityGroup, AwsElasticsearchDomain, AwsLambdaLayerVersion, AwsRdsDbInstance, and AwsWafWebAcl. The following resource types are added without an accompanying details object: AutoscalingAutoscalingGroup, AwsDynamoDbTable, AwsEc2Eip, AwsEc2Snapshot, AwsEc2Volume, AwsRdsDbSnapshot, AwsRedshiftCluster, and AwsS3Object. The number of allowed resources per finding is increased from 10 to 32. A new field is added in the Compliance object, RelatedRequirements. To learn more, visit our documentation on the ASFF." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.59.json b/.changes/2.10.59.json new file mode 100644 index 000000000000..131bac6dd0a2 --- /dev/null +++ b/.changes/2.10.59.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.59", + "date": "2020-02-06", + "entries": [ + { + "type": "feature", + "category": "AWS CodeBuild", + "description": "AWS CodeBuild adds support for Amazon Elastic File Systems" + }, + { + "type": "feature", + "category": "AWS AppSync", + "description": "AWS AppSync now supports X-Ray" + }, + { + "type": "feature", + "category": "Amazon Elastic Block Store", + "description": "Documentation updates for EBS direct APIs." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "In order to make operations more easily discoverable by an IDE, specific operation methods have been added to the enhanced client interface. An operation method takes a corresponding request object as parameter. Meanwhile, the generic execute() method is removed. This change affects only batch and transcribe operations at the database level." + }, + { + "type": "feature", + "category": "Amazon EC2 Container Registry", + "description": "This release contains updated text for the GetAuthorizationToken API." + }, + { + "type": "feature", + "category": "Amazon Lex Model Building Service", + "description": "Amazon Lex now supports AMAZON.AlphaNumeric with regular expressions." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds platform details and billing info to the DescribeImages API." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.60.json b/.changes/2.10.60.json new file mode 100644 index 000000000000..7daeb137527c --- /dev/null +++ b/.changes/2.10.60.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.60", + "date": "2020-02-07", + "entries": [ + { + "type": "feature", + "category": "AWS RoboMaker", + "description": "This release adds support for simulation job batches" + }, + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "Documentation updates for RDS: when restoring a DB cluster from a snapshot, must create DB instances" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "EC2 Image Builder", + "description": "This version of the SDK includes bug fixes and documentation updates." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Improves discoverability by renaming the table and index interfaces to be consistent with the client interface naming, and by adding operation methods for createTable(), scan() and query(), as applicable. These methods take a request object as parameter. Execute() methods for the index interface is removed since they are no longer needed." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.61.json b/.changes/2.10.61.json new file mode 100644 index 000000000000..8fbb38ecde08 --- /dev/null +++ b/.changes/2.10.61.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.61", + "date": "2020-02-10", + "entries": [ + { + "type": "feature", + "category": "Amazon DocumentDB with MongoDB compatibility", + "description": "Added clarifying information that Amazon DocumentDB shares operational technology with Amazon RDS and Amazon Neptune." + }, + { + "type": "feature", + "category": "AWS Key Management Service", + "description": "The ConnectCustomKeyStore API now provides a new error code (SUBNET_NOT_FOUND) for customers to better troubleshoot if their \"connect-custom-key-store\" operation fails." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.62.json b/.changes/2.10.62.json new file mode 100644 index 000000000000..ee20a57c70c1 --- /dev/null +++ b/.changes/2.10.62.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.62", + "date": "2020-02-11", + "entries": [ + { + "type": "feature", + "category": "AWS CloudFormation", + "description": "This release of AWS CloudFormation StackSets allows you to centrally manage deployments to all the accounts in your organization or specific organizational units (OUs) in AWS Organizations. You will also be able to enable automatic deployments to any new accounts added to your organization or OUs. The permissions needed to deploy across accounts will automatically be taken care of by the StackSets service." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Amazon EC2 Now Supports Tagging Spot Fleet." + }, + { + "type": "feature", + "category": "Amazon Cognito Identity Provider", + "description": "Features:This release adds a new setting for a user pool to allow if customer wants their user signup/signin with case insensitive username. The current default setting is case sensitive, and for our next release we will change it to case insensitive." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.63.json b/.changes/2.10.63.json new file mode 100644 index 000000000000..4bb888d063d0 --- /dev/null +++ b/.changes/2.10.63.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.63", + "date": "2020-02-12", + "entries": [ + { + "type": "feature", + "category": "Amazon Neptune", + "description": "This launch enables Neptune start-db-cluster and stop-db-cluster. Stopping and starting Amazon Neptune clusters helps you manage costs for development and test environments. You can temporarily stop all the DB instances in your cluster, instead of setting up and tearing down all the DB instances each time that you use the cluster." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release adds support for tagging public IPv4 pools." + }, + { + "type": "feature", + "category": "Amazon Chime", + "description": "Documentation updates for Amazon Chime" + }, + { + "type": "feature", + "category": "AWS Directory Service", + "description": "Release to add the ExpirationDateTime as an output to ListCertificates so as to ease customers to look into their certificate lifetime and make timely decisions about renewing them." + }, + { + "type": "feature", + "category": "Amazon Elasticsearch Service", + "description": "Amazon Elasticsearch Service now offers fine-grained access control, which adds multiple capabilities to give tighter control over data. New features include the ability to use roles to define granular permissions for indices, documents, or fields and to extend Kibana with read-only views and secure multi-tenant support." + }, + { + "type": "feature", + "category": "Amazon WorkMail", + "description": "This release adds support for access control rules management in Amazon WorkMail." + }, + { + "type": "feature", + "category": "AWS Glue", + "description": "Adding ability to add arguments that cannot be overridden to AWS Glue jobs" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.64.json b/.changes/2.10.64.json new file mode 100644 index 000000000000..1f93f2b9406a --- /dev/null +++ b/.changes/2.10.64.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.64", + "date": "2020-02-13", + "entries": [ + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Improves discoverability by adding operation methods for deleteItem(), getItem(), putItem and updateItem(), as applicable. These methods take a request object as parameter. Execute() methods for the table interface is removed since they are no longer needed." + }, + { + "type": "feature", + "category": "Netty NIO HTTP Client", + "description": "When there is an I/O error on an http2 request, the SDK will start shutting down the connection - stopping using the http2 connection for new requests and closing it after all streams are finished." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS Elemental MediaPackage VOD", + "description": "Adds support for DASH with multiple media presentation description periods triggered by presence of SCTE-35 ad markers in the manifest.Also adds optional configuration for DASH SegmentTemplateFormat to refer to segments by Number with Duration, Number with Timeline or Time with Timeline and compact the manifest by combining duplicate SegmentTemplate tags." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.65.json b/.changes/2.10.65.json new file mode 100644 index 000000000000..ae1367df3b1d --- /dev/null +++ b/.changes/2.10.65.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.65", + "date": "2020-02-14", + "entries": [ + { + "type": "feature", + "category": "AWS MediaTailor", + "description": "AWS Elemental MediaTailor SDK now allows configuration of Personalization Threshold for HLS and DASH streams." + }, + { + "type": "feature", + "category": "AWS Shield", + "description": "This release adds support for associating Amazon Route 53 health checks to AWS Shield Advanced protected resources." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "You can now enable Multi-Attach on Provisioned IOPS io1 volumes through the create-volume API." + }, + { + "type": "feature", + "category": "Amazon S3", + "description": "Added support for presigning `CreateMultipartUpload`, `UploadPart`, `CompleteMultipartUpload`, and `AbortMultipartUpload` requests." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "Security Hub has released a new DescribeStandards API action. This API action allows a customer to list all of the standards available in an account. For each standard, the list provides the customer with the standard name, description, and ARN. Customers can use the ARN as an input to the BatchEnableStandards API action. To learn more, visit our API documentation." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.66.json b/.changes/2.10.66.json new file mode 100644 index 000000000000..bf3812e1dffd --- /dev/null +++ b/.changes/2.10.66.json @@ -0,0 +1,31 @@ +{ + "version": "2.10.66", + "date": "2020-02-17", + "entries": [ + { + "type": "feature", + "category": "AWS Cloud9", + "description": "AWS Cloud9 now supports the ability to tag Cloud9 development environments." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Documentation updates for EC2" + }, + { + "type": "feature", + "category": "Amazon DynamoDB", + "description": "Amazon DynamoDB enables you to restore your DynamoDB backup or table data across AWS Regions such that the restored table is created in a different AWS Region from where the source table or backup resides. You can do cross-region restores between AWS commercial Regions, AWS China Regions, and AWS GovCloud (US) Regions." + }, + { + "type": "feature", + "category": "Amazon Rekognition", + "description": "This update adds the ability to detect text in videos and adds filters to image and video text detection." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.67.json b/.changes/2.10.67.json new file mode 100644 index 000000000000..5e45aa097449 --- /dev/null +++ b/.changes/2.10.67.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.67", + "date": "2020-02-18", + "entries": [ + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "This release supports Microsoft Active Directory authentication for Amazon Aurora." + }, + { + "type": "feature", + "category": "Auto Scaling", + "description": "Amazon EC2 Auto Scaling now supports the ability to enable/disable target tracking, step scaling, and simple scaling policies." + }, + { + "type": "feature", + "category": "Amazon Chime", + "description": "Added AudioFallbackUrl to support Chime SDK client." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.68.json b/.changes/2.10.68.json new file mode 100644 index 000000000000..ea76797ecf61 --- /dev/null +++ b/.changes/2.10.68.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.68", + "date": "2020-02-19", + "entries": [ + { + "type": "feature", + "category": "AWS Lambda", + "description": "AWS Lambda now supports Ruby 2.7" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Auto Scaling", + "description": "Doc update for EC2 Auto Scaling: Add Enabled parameter for PutScalingPolicy" + }, + { + "type": "feature", + "category": "AWS Service Catalog", + "description": "\"ListPortfolioAccess\" API now has a new optional parameter \"OrganizationParentId\". When it is provided and if the portfolio with the \"PortfolioId\" given was shared with an organization or organizational unit with \"OrganizationParentId\", all accounts in the organization sub-tree under parent which inherit an organizational portfolio share will be listed, rather than all accounts with external shares. To accommodate long lists returned from the new option, the API now supports pagination." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.69.json b/.changes/2.10.69.json new file mode 100644 index 000000000000..468ca3ee42f2 --- /dev/null +++ b/.changes/2.10.69.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.69", + "date": "2020-02-20", + "entries": [ + { + "type": "feature", + "category": "AWS Savings Plans", + "description": "Added support for AWS Lambda in Compute Savings Plans" + }, + { + "type": "feature", + "category": "Amazon Pinpoint", + "description": "As of this release of the Amazon Pinpoint API, the Title property is optional for the CampaignEmailMessage object." + }, + { + "type": "feature", + "category": "Amazon AppConfig", + "description": "This release adds exponential growth type support for deployment strategies." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.7.json b/.changes/2.10.7.json new file mode 100644 index 000000000000..3b7657d25986 --- /dev/null +++ b/.changes/2.10.7.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.7", + "date": "2019-11-01", + "entries": [ + { + "type": "feature", + "category": "Amazon Pinpoint", + "description": "This release of the Amazon Pinpoint API introduces support for using and managing journeys, and querying analytics data for journeys." + }, + { + "type": "feature", + "category": "AWS CloudTrail", + "description": "This release adds two new APIs, GetTrail and ListTrails, and support for adding tags when you create a trail by using a new TagsList parameter on CreateTrail operations." + }, + { + "type": "feature", + "category": "AWS Database Migration Service", + "description": "This release contains task timeline attributes in replication task statistics. This release also adds a note to the documentation for the CdcStartPosition task request parameter. This note describes how to enable the use of native CDC start points for a PostgreSQL source by setting the new slotName extra connection attribute on the source endpoint to the name of an existing logical replication slot." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.70.json b/.changes/2.10.70.json new file mode 100644 index 000000000000..6d775f264f33 --- /dev/null +++ b/.changes/2.10.70.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.70", + "date": "2020-02-21", + "entries": [ + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Improves discoverability by adding consumer-style methods for all client, table and index operations." + }, + { + "type": "feature", + "category": "EC2 Image Builder", + "description": "This release of EC2 Image Builder increases the maximum policy document size for Image Builder resource-based policy APIs." + }, + { + "type": "feature", + "category": "Amazon Redshift", + "description": "Extend elastic resize to support resizing clusters to different instance types." + }, + { + "type": "feature", + "category": "AWS WAFV2", + "description": "Documentation updates for AWS WAF (wafv2) to correct the guidance for associating a web ACL to a CloudFront distribution." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.71.json b/.changes/2.10.71.json new file mode 100644 index 000000000000..7160a6a2ec6d --- /dev/null +++ b/.changes/2.10.71.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.71", + "date": "2020-02-24", + "entries": [ + { + "type": "feature", + "category": "Amazon DocumentDB with MongoDB compatibility", + "description": "Documentation updates for docdb" + }, + { + "type": "feature", + "category": "AWS IoT Events", + "description": "Documentation updates for iotcolumbo" + }, + { + "type": "feature", + "category": "Amazon FSx", + "description": "Announcing persistent file systems for Amazon FSx for Lustre that are ideal for longer-term storage and workloads, and a new generation of scratch file systems that offer higher burst throughput for spiky workloads." + }, + { + "type": "feature", + "category": "Amazon CloudWatch Events", + "description": "This release allows you to create and manage tags for event buses." + }, + { + "type": "feature", + "category": "Amazon EventBridge", + "description": "This release allows you to create and manage tags for event buses." + }, + { + "type": "feature", + "category": "Amazon Import/Export Snowball", + "description": "AWS Snowball adds a field for entering your GSTIN when creating AWS Snowball jobs in the Asia Pacific (Mumbai) region." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.72.json b/.changes/2.10.72.json new file mode 100644 index 000000000000..8d4773e1d13e --- /dev/null +++ b/.changes/2.10.72.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.72", + "date": "2020-02-25", + "entries": [ + { + "type": "feature", + "category": "AWS Secrets Manager", + "description": "This release increases the maximum allowed size of SecretString or SecretBinary from 10KB to 64KB in the CreateSecret, UpdateSecret, PutSecretValue and GetSecretValue APIs." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Improves discoverability by adding consumer-style methods for all client, table and index operations." + }, + { + "type": "feature", + "category": "AWS Step Functions", + "description": "This release adds support for CloudWatch Logs for Standard Workflows." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Managed Streaming for Kafka", + "description": "Amazon MSK has added support for Broker Log delivery to CloudWatch, S3, and Firehose." + }, + { + "type": "feature", + "category": "AWS Outposts", + "description": "This release adds DeleteSite and DeleteOutpost." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.73.json b/.changes/2.10.73.json new file mode 100644 index 000000000000..f1d6943a2737 --- /dev/null +++ b/.changes/2.10.73.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.73", + "date": "2020-02-26", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release changes the RunInstances CLI and SDK's so that if you do not specify a client token, a randomly generated token is used for the request to ensure idempotency." + }, + { + "type": "feature", + "category": "Amazon SageMaker Service", + "description": "SageMaker UpdateEndpoint API now supports retained variant properties, e.g., instance count, variant weight. SageMaker ListTrials API filter by TrialComponentName. Make ExperimentConfig name length limits consistent with CreateExperiment, CreateTrial, and CreateTrialComponent APIs." + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "Amazon Transcribe's Automatic Content Redaction feature enables you to automatically redact sensitive personally identifiable information (PII) from transcription results. It replaces each instance of an identified PII utterance with a [PII] tag in the transcript." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "Security Hub has added to the DescribeProducts API operation a new response field called IntegrationTypes. The IntegrationTypes field lists the types of actions that a product performs relative to Security Hub such as send findings to Security Hub and receive findings from Security Hub." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Added the BeanTableSchema implementation of TableSchema that allows a TableSchema to be instantiated from an annotated Java bean class which can then be used with the DynamoDB Enhanced Client." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.74.json b/.changes/2.10.74.json new file mode 100644 index 000000000000..532b19f173c2 --- /dev/null +++ b/.changes/2.10.74.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.74", + "date": "2020-02-27", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS Global Accelerator", + "description": "This release adds support for adding tags to accelerators and bringing your own IP address to AWS Global Accelerator (BYOIP)." + }, + { + "type": "feature", + "category": "Amazon Lightsail", + "description": "Adds support to create notification contacts in Amazon Lightsail, and to create instance, database, and load balancer metric alarms that notify you based on the value of a metric relative to a threshold that you specify." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.75.json b/.changes/2.10.75.json new file mode 100644 index 000000000000..eeccf6eea273 --- /dev/null +++ b/.changes/2.10.75.json @@ -0,0 +1,51 @@ +{ + "version": "2.10.75", + "date": "2020-02-28", + "entries": [ + { + "type": "feature", + "category": "Amazon QuickSight", + "description": "Added SearchDashboards API that allows listing of dashboards that a specific user has access to." + }, + { + "type": "feature", + "category": "AWS Glue", + "description": "AWS Glue adds resource tagging support for Machine Learning Transforms and adds a new API, ListMLTransforms to support tag filtering. With this feature, customers can use tags in AWS Glue to organize and control access to Machine Learning Transforms." + }, + { + "type": "feature", + "category": "Elastic Load Balancing", + "description": "Added a target group attribute to support sticky sessions for Network Load Balancers." + }, + { + "type": "feature", + "category": "Access Analyzer", + "description": "This release includes improvements and fixes bugs for the IAM Access Analyzer feature." + }, + { + "type": "feature", + "category": "Amazon CodeGuru Profiler", + "description": "Documentation updates for Amazon CodeGuru Profiler" + }, + { + "type": "feature", + "category": "AWS App Mesh", + "description": "App Mesh now supports Transport Layer Security (TLS) between Virtual Nodes in a Mesh. Customers can use managed certificates from an AWS Certificate Manager Private Certificate Authority or bring their own certificates from the local file system to encrypt traffic between their workloads. See https://docs.aws.amazon.com/app-mesh/latest/userguide/virtual-node-tls.html for details." + }, + { + "type": "feature", + "category": "AWS Config", + "description": "Accepts a structured query language (SQL) SELECT command and an aggregator name, performs the corresponding search on resources aggregated by the aggregator, and returns resource configurations matching the properties." + }, + { + "type": "feature", + "category": "Amazon Augmented AI Runtime", + "description": "This release updates Amazon Augmented AI ListHumanLoops API, DescribeHumanLoop response, StartHumanLoop response and type names of SDK fields." + }, + { + "type": "feature", + "category": "Amazon WorkDocs", + "description": "Documentation updates for workdocs" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.76.json b/.changes/2.10.76.json new file mode 100644 index 000000000000..60ce783decd5 --- /dev/null +++ b/.changes/2.10.76.json @@ -0,0 +1,11 @@ +{ + "version": "2.10.76", + "date": "2020-02-28", + "entries": [ + { + "type": "feature", + "category": "AWS Config", + "description": "Correcting list of supported resource types." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.77.json b/.changes/2.10.77.json new file mode 100644 index 000000000000..adfc3c738537 --- /dev/null +++ b/.changes/2.10.77.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.77", + "date": "2020-03-02", + "entries": [ + { + "type": "feature", + "category": "Amazon CloudWatch", + "description": "Introducing Amazon CloudWatch Composite Alarms" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS Comprehend Medical", + "description": "New Time Expression feature, part of DetectEntitiesV2 API will provide temporal relations to existing NERe entities such as Medication, Test, Treatment, Procedure and Medical conditions." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.78.json b/.changes/2.10.78.json new file mode 100644 index 000000000000..9701522dba63 --- /dev/null +++ b/.changes/2.10.78.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.78", + "date": "2020-03-03", + "entries": [ + { + "type": "bugfix", + "category": "Netty NIO HTTP Client", + "description": "Fix an issue where the Netty client was prematurely considering an HTTP/2 request body as sent, but was still in the process of being transferred to the remote endpoint." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Amazon VPC Flow Logs adds support for tags and tagging on resource creation." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.79.json b/.changes/2.10.79.json new file mode 100644 index 000000000000..97fe6ea69f16 --- /dev/null +++ b/.changes/2.10.79.json @@ -0,0 +1,11 @@ +{ + "version": "2.10.79", + "date": "2020-03-04", + "entries": [ + { + "type": "feature", + "category": "Amazon Pinpoint", + "description": "This release of the Amazon Pinpoint API introduces support for integrating recommender models with email, push notification, and SMS message templates. You can now use these types of templates to connect to recommender models and add personalized recommendations to messages that you send from campaigns and journeys." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.8.json b/.changes/2.10.8.json new file mode 100644 index 000000000000..7347f9ec3b50 --- /dev/null +++ b/.changes/2.10.8.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.8", + "date": "2019-11-04", + "entries": [ + { + "type": "feature", + "category": "AWS RoboMaker", + "description": "RoboMaker Fleet Management launch a feature to verify your robot is ready to download and install the new robot application using a download condition file, which is a script run on the robot prior to downloading the new deployment." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Accelerator (DAX)", + "description": "Documentation updates for dax" + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Documentation updates for ec2" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.80.json b/.changes/2.10.80.json new file mode 100644 index 000000000000..7502199d9427 --- /dev/null +++ b/.changes/2.10.80.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.80", + "date": "2020-03-05", + "entries": [ + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Adds javadoc to operation methods and request/response objects." + }, + { + "type": "feature", + "category": "Amazon GuardDuty", + "description": "Add a new finding field for EC2 findings indicating the instance's local IP address involved in the threat." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS OpsWorks CM", + "description": "Updated the Tag regex pattern to align with AWS tagging APIs." + }, + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Amazon EKS now supports adding a KMS key to your cluster for envelope encryption of Kubernetes secrets." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "You can now create AWS Client VPN Endpoints with a specified VPC and Security Group. Additionally, you can modify these attributes when modifying the endpoint." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.81.json b/.changes/2.10.81.json new file mode 100644 index 000000000000..a1860553e473 --- /dev/null +++ b/.changes/2.10.81.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.81", + "date": "2020-03-06", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "This release provides customers with a self-service option to enable Local Zones." + }, + { + "type": "feature", + "category": "AWS RoboMaker", + "description": "Added support for streaming a GUI from robot and simulation applications" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon GuardDuty", + "description": "Amazon GuardDuty findings now include the OutpostArn if the finding is generated for an AWS Outposts EC2 host." + }, + { + "type": "bugfix", + "category": "Netty NIO Http Client", + "description": "Expand Http2 connection-level flow control window when a new stream is acquired on that connection so that the connection-level window size is proportional to the number of streams." + }, + { + "type": "feature", + "category": "AWS Signer", + "description": "This release enables signing image format override in PutSigningProfile requests, adding two more enum fields, JSONEmbedded and JSONDetached. This release also extends the length limit of SigningProfile name from 20 to 64." + }, + { + "type": "feature", + "category": "AWS App Mesh", + "description": "App Mesh now supports sharing a Mesh with other AWS accounts. Customers can use AWS Resource Access Manager to share their Mesh with other accounts in their organization to connection applications within a single service mesh. See https://docs.aws.amazon.com/app-mesh/latest/userguide/sharing.html for details." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.82.json b/.changes/2.10.82.json new file mode 100644 index 000000000000..d8424bd86bca --- /dev/null +++ b/.changes/2.10.82.json @@ -0,0 +1,46 @@ +{ + "version": "2.10.82", + "date": "2020-03-09", + "entries": [ + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Use the last seen HTTP/1.1 header value for headers defined to only appear once in an HTTP message instead of merging them all into a list. The order in which header values are inspected is: headers set by the request marshaller, overridden headers set on the client, then finally overridden headers set on the SDK request object. See https://tools.ietf.org/html/rfc2616#section-4.2 for more information." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Added support for \"retry modes\". A retry mode allows configuring multiple SDK parameters at once using default retry profiles, some of which are standardized between AWS SDK languages. See RetryMode javadoc for more information." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Fixed an issue where specifying your own retry policy would override AWS and service-specific retry conditions. By default, all retry policies now have AWS and service-specific retry conditions added. This can be disabled via the new `RetryPolicy.furtherRefinementsAllowed(false)`." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Fixed an issue where the retry condition returned by `RetryPolicy.retryCondition` differed from the one specified by `RetryPolicy.Builder.retryCondition`. The old value can be accessed via the new `RetryPolicy.aggregateRetryCondition`." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Added the ability to configure or disable the default retry throttling behavior of the SDK that 'kicks in' during a large volume of retriable service call errors. This behavior can now be configured via `RetryPolicy.retryCapacityCondition`." + }, + { + "type": "feature", + "category": "AWS Elemental MediaLive", + "description": "AWS Elemental MediaLive now supports the ability to configure the Preferred Channel Pipeline for channels contributing to a Multiplex." + }, + { + "type": "feature", + "category": "AWS Database Migration Service", + "description": "Added new settings for Kinesis target to include detailed transaction info; to capture table DDL details; to use single-line unformatted json, which can be directly queried by AWS Athena if data is streamed into S3 through AWS Kinesis Firehose. Added CdcInsertsAndUpdates in S3 target settings to allow capture ongoing insertions and updates only." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Amazon Virtual Private Cloud (VPC) NAT Gateway adds support for tagging on resource creation." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.83.json b/.changes/2.10.83.json new file mode 100644 index 000000000000..b9cff19f5f85 --- /dev/null +++ b/.changes/2.10.83.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.83", + "date": "2020-03-10", + "entries": [ + { + "type": "feature", + "category": "AWS Marketplace Commerce Analytics", + "description": "Change the disbursement data set to look past 31 days instead until the beginning of the month." + }, + { + "type": "feature", + "category": "Amazon Transcribe Service", + "description": "Amazon Transcribe's Automatic Content Redaction feature enables you to automatically redact sensitive personally identifiable information (PII) from transcription results. It replaces each instance of an identified PII utterance with a [PII] tag in the transcript." + }, + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Documentation updates for EC2" + }, + { + "type": "feature", + "category": "AWSServerlessApplicationRepository", + "description": "AWS Serverless Application Repository now supports sharing applications privately with AWS Organizations." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Reverts a recent change from 2.10.70 where the json protocol type was changed to application/json, this is now back to application/x-amz-json-1.1." + }, + { + "type": "feature", + "category": "AWS IoT Events", + "description": "API update that adds a new parameter, durationExpression, to SetTimerAction, and deprecates seconds" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.84.json b/.changes/2.10.84.json new file mode 100644 index 000000000000..b66000168d94 --- /dev/null +++ b/.changes/2.10.84.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.84", + "date": "2020-03-11", + "entries": [ + { + "type": "feature", + "category": "Amazon Redshift", + "description": "Amazon Redshift now supports operations to pause and resume a cluster on demand or on a schedule." + }, + { + "type": "feature", + "category": "Amazon Elastic File System", + "description": "Documentation updates for elasticfilesystem" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.85.json b/.changes/2.10.85.json new file mode 100644 index 000000000000..f34778978c26 --- /dev/null +++ b/.changes/2.10.85.json @@ -0,0 +1,36 @@ +{ + "version": "2.10.85", + "date": "2020-03-12", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Compute Cloud", + "description": "Documentation updates for EC2" + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "As part of this release, we are extending capability of AWS IoT Rules Engine to support IoT Cloudwatch log action. The IoT Cloudwatch log rule action lets you send messages from IoT sensors and applications to Cloudwatch logs for troubleshooting and debugging." + }, + { + "type": "feature", + "category": "Amazon Lex Model Building Service", + "description": "Amazon Lex now supports tagging for bots, bot aliases and bot channels." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AmazonApiGatewayV2", + "description": "Amazon API Gateway HTTP APIs is now generally available. HTTP APIs offer the core functionality of REST API at up to 71% lower price compared to REST API, 60% lower p99 latency, and is significantly easier to use. As part of general availability, we added new features to route requests to private backends such as private ALBs, NLBs, and IP/ports. We also brought over a set of features from REST API such as Stage Variables, and Stage/Route level throttling. Custom domain names can also now be used with both REST And HTTP APIs." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "The AWS Security Finding Format is being augmented with the following changes. 21 new resource types without corresponding details objects are added. Another new resource type, AwsS3Object, has an accompanying details object. Severity.Label is a new string field that indicates the severity of a finding. The available values are: INFORMATIONAL, LOW, MEDIUM, HIGH, CRITICAL. The new string field Workflow.Status indicates the status of the investigation into a finding. The available values are: NEW, NOTIFIED, RESOLVED, SUPPRESSED." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.86.json b/.changes/2.10.86.json new file mode 100644 index 000000000000..de89348d4a8e --- /dev/null +++ b/.changes/2.10.86.json @@ -0,0 +1,11 @@ +{ + "version": "2.10.86", + "date": "2020-03-13", + "entries": [ + { + "type": "feature", + "category": "Amazon AppConfig", + "description": "This release adds S3 as a configuration source provider." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.87.json b/.changes/2.10.87.json new file mode 100644 index 000000000000..1290e9d73f89 --- /dev/null +++ b/.changes/2.10.87.json @@ -0,0 +1,41 @@ +{ + "version": "2.10.87", + "date": "2020-03-16", + "entries": [ + { + "type": "feature", + "category": "Amazon Simple Systems Manager (SSM)", + "description": "Resource data sync for AWS Systems Manager Inventory now includes destination data sharing. This feature enables you to synchronize inventory data from multiple AWS accounts into a central Amazon S3 bucket. To use this feature, all AWS accounts must be listed in AWS Organizations." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon EC2 Container Service", + "description": "This release adds the ability to update the task placement strategy and constraints for Amazon ECS services." + }, + { + "type": "feature", + "category": "Amazon ElastiCache", + "description": "Amazon ElastiCache now supports Global Datastore for Redis. Global Datastore for Redis offers fully managed, fast, reliable and secure cross-region replication. Using Global Datastore for Redis, you can create cross-region read replica clusters for ElastiCache for Redis to enable low-latency reads and disaster recovery across regions. You can create, modify and describe a Global Datastore, as well as add or remove regions from your Global Datastore and promote a region as primary in Global Datastore." + }, + { + "type": "feature", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "The enhanced DDB client table schema now supports custom AttributeConverterProviders, and StaticAttribute can take individual AttributeConverter to override default attribute converter behavior." + }, + { + "type": "feature", + "category": "AWS S3 Control", + "description": "Amazon S3 now supports Batch Operations job tagging." + }, + { + "type": "feature", + "category": "Amazon Cognito Identity Provider", + "description": "Additional response field \"CompromisedCredentialsDetected\" added to AdminListUserAuthEvents." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.88.json b/.changes/2.10.88.json new file mode 100644 index 000000000000..018bc306c56b --- /dev/null +++ b/.changes/2.10.88.json @@ -0,0 +1,21 @@ +{ + "version": "2.10.88", + "date": "2020-03-17", + "entries": [ + { + "type": "feature", + "category": "AWS Elemental MediaConvert", + "description": "AWS Elemental MediaConvert SDK has added support for: AV1 encoding in File Group MP4, DASH and CMAF DASH outputs; PCM/WAV audio output in MPEG2-TS containers; and Opus audio in Webm inputs." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Fix an issue where the signing key is created only once at the start of the request for event streaming requests. This causes requests that span two or more days to have signing errors once the date changes because the signing key was derived only once using the date at the beginning of the request." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.89.json b/.changes/2.10.89.json new file mode 100644 index 000000000000..51e74182eb77 --- /dev/null +++ b/.changes/2.10.89.json @@ -0,0 +1,26 @@ +{ + "version": "2.10.89", + "date": "2020-03-18", + "entries": [ + { + "type": "feature", + "category": "Amazon Personalize", + "description": "[Personalize] Adds support for returning hyperparameter values of the best performing model in a HPO job." + }, + { + "type": "feature", + "category": "AWS MediaConnect", + "description": "Feature adds the ability for a flow to have multiple redundant sources that provides resiliency to a source failing. The new APIs added to enable the feature are, AddFlowSources, RemoveFlowSource and UpdateFlow." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "Updated the MaxRecords type in DescribeExportTasks to Integer." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.9.json b/.changes/2.10.9.json new file mode 100644 index 000000000000..edfdcbe9a4a3 --- /dev/null +++ b/.changes/2.10.9.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.9", + "date": "2019-11-05", + "entries": [ + { + "type": "feature", + "category": "AWS CodeStar Notifications", + "description": "This release adds a notification manager for events in repositories, build projects, deployments, and pipelines. You can now configure rules and receive notifications about events that occur for resources. Each notification includes a status message as well as a link to the resource (repository, build project, deployment application, or pipeline) whose event generated the notification." + }, + { + "type": "feature", + "category": "Amazon Relational Database Service", + "description": "Documentation updates for Amazon RDS" + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.90.json b/.changes/2.10.90.json new file mode 100644 index 000000000000..1bc2885b9927 --- /dev/null +++ b/.changes/2.10.90.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.90", + "date": "2020-03-19", + "entries": [ + { + "type": "feature", + "category": "AWS Outposts", + "description": "Documentation updates for AWS Outposts." + }, + { + "type": "feature", + "category": "AWS Certificate Manager", + "description": "AWS Certificate Manager documentation updated on API calls ImportCertificate and ListCertificate. Specific updates included input constraints, private key size for import and next token size for list." + } + ] +} \ No newline at end of file diff --git a/.changes/2.10.91.json b/.changes/2.10.91.json new file mode 100644 index 000000000000..d9deebd17e5a --- /dev/null +++ b/.changes/2.10.91.json @@ -0,0 +1,16 @@ +{ + "version": "2.10.91", + "date": "2020-03-20", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS Service Catalog", + "description": "Added \"productId\" and \"portfolioId\" to responses from CreateConstraint, UpdateConstraint, ListConstraintsForPortfolio, and DescribeConstraint APIs" + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.0.json b/.changes/2.11.0.json new file mode 100644 index 000000000000..c7ab334f3017 --- /dev/null +++ b/.changes/2.11.0.json @@ -0,0 +1,31 @@ +{ + "version": "2.11.0", + "date": "2020-03-23", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Adding new error code IamLimitExceeded for Nodegroups in EKS" + }, + { + "type": "feature", + "category": "AmazonApiGatewayV2", + "description": "Documentation updates to reflect that the default timeout for integrations is now 30 seconds for HTTP APIs." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updating dependency version: netty 4.1.42.Final -> 4.1.46.Final (contains the fix for reducing heap usage for netty client)" + }, + { + "type": "feature", + "category": "Amazon Route 53", + "description": "Documentation updates for Route 53." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Bump minor version to '2.11.0-SNAPSHOT' because of [#1692](https://github.com/aws/aws-sdk-java-v2/issues/1692)" + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.1.json b/.changes/2.11.1.json new file mode 100644 index 000000000000..9100677a3b14 --- /dev/null +++ b/.changes/2.11.1.json @@ -0,0 +1,31 @@ +{ + "version": "2.11.1", + "date": "2020-03-24", + "entries": [ + { + "type": "feature", + "category": "Amazon Elastic Kubernetes Service", + "description": "Adding new error codes: Ec2SubnetInvalidConfiguration and NodeCreationFailure for Nodegroups in EKS" + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS Organizations", + "description": "Introduces actions for giving a member account administrative Organizations permissions for an AWS service. You can run this action only for AWS services that support this feature." + }, + { + "type": "feature", + "category": "AWS RDS DataService", + "description": "Documentation updates for rds-data" + }, + { + "type": "feature", + "category": "Amazon Athena", + "description": "Documentation updates for Athena, including QueryExecutionStatus QUEUED and RUNNING states. QUEUED now indicates that the query has been submitted to the service. RUNNING indicates that the query is in execution phase." + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.2.json b/.changes/2.11.2.json new file mode 100644 index 000000000000..93008d01a6b2 --- /dev/null +++ b/.changes/2.11.2.json @@ -0,0 +1,76 @@ +{ + "version": "2.11.2", + "date": "2020-03-25", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Reduced the number of times the profile file configuration is read from disk on client creation from 3-5 to 1." + }, + { + "type": "feature", + "category": "AWS X-Ray", + "description": "GetTraceSummaries - Now provides additional root cause attribute ClientImpacting which indicates whether root cause impacted trace client." + }, + { + "type": "feature", + "category": "Amazon Elasticsearch Service", + "description": "Adding support for customer packages (dictionary files) to Amazon Elasticsearch Service" + }, + { + "type": "bugfix", + "category": "Amazon DynamoDB", + "description": "Fixed an issue that could cause a null-pointer-exception when using anonymous credentials with endpoint discovery enabled." + }, + { + "type": "feature", + "category": "Amazon Managed Blockchain", + "description": "Amazon Managed Blockchain now has support to publish Hyperledger Fabric peer node, chaincode, and certificate authority (CA) logs to Amazon CloudWatch Logs." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "bugfix", + "category": "Amazon DynamoDB Enhanced Client [Preview]", + "description": "Performance improvements." + }, + { + "type": "feature", + "category": "AWS Cost Explorer Service", + "description": "Customers can now receive Savings Plans recommendations at the member (linked) account level." + }, + { + "type": "feature", + "category": "Amazon Detective", + "description": "The new ACCEPTED_BUT_DISABLED member account status indicates that a member account that accepted the invitation is blocked from contributing data to the behavior graph. The reason is provided in the new DISABLED_REASON property. The new StartMonitoringMember operation enables a blocked member account." + }, + { + "type": "feature", + "category": "Amazon CloudWatch Application Insights", + "description": "Amazon CloudWatch Application Insights for .NET and SQL Server now integrates with Amazon CloudWatch Events (AWS CodeDeploy, AWS Health and Amazon EC2 state changes). This feature enables customers to view events related to problems detected by CloudWatch Application Insights, and reduce mean-time-to-resolution (MTTR)." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Added a `defaultProfileFile` and `defaultProfileName` option to the client override configuration. Setting this configuration value is equivalent to setting the environment or system properties for the profile file and profile name. Specifically, it sets the default profile file and profile name used by the client." + }, + { + "type": "bugfix", + "category": "Amazon S3", + "description": "Fixed a bug where explicitly disabling use-arn-region on S3Configuration would have lower priority than the environment variable, system property or profile property." + }, + { + "type": "bugfix", + "category": "Amazon DynamoDB", + "description": "Fixed an issue where endpoint discovery configuration specified in the profile file was being ignored." + }, + { + "type": "feature", + "category": "Amazon DynamoDB", + "description": "When endpoint discovery is enabled, the endpoint discovery process is now initialized with the first request, instead of 60 seconds after the first request." + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.3.json b/.changes/2.11.3.json new file mode 100644 index 000000000000..615edf7e4152 --- /dev/null +++ b/.changes/2.11.3.json @@ -0,0 +1,26 @@ +{ + "version": "2.11.3", + "date": "2020-03-26", + "entries": [ + { + "type": "feature", + "category": "Amazon SageMaker Service", + "description": "This release updates Amazon Augmented AI CreateFlowDefinition API and DescribeFlowDefinition response." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS SecurityHub", + "description": "Security Hub has now made it easier to opt out of default standards when you enable Security Hub. We added a new Boolean parameter to EnableSecurityHub called EnableDefaultStandards. If that parameter is true, Security Hub's default standards are enabled. A new Boolean parameter for standards, EnabledByDefault, indicates whether a standard is a default standard. Today, the only default standard is CIS AWS Foundations Benchmark v1.2. Additional default standards will be added in the future.To learn more, visit our documentation on the EnableSecurityHub API action." + }, + { + "type": "feature", + "category": "Amazon FSx", + "description": "This release includes two changes: a new lower-cost, storage type called HDD (Hard Disk Drive), and a new generation of the Single-AZ deployment type called Single AZ 2. The HDD storage type can be selected on Multi AZ 1 and Single AZ 2 deployment types." + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.4.json b/.changes/2.11.4.json new file mode 100644 index 000000000000..dbab31a8ff86 --- /dev/null +++ b/.changes/2.11.4.json @@ -0,0 +1,31 @@ +{ + "version": "2.11.4", + "date": "2020-03-27", + "entries": [ + { + "type": "feature", + "category": "AWS Service Catalog", + "description": "Added \"LocalRoleName\" as an acceptable Parameter for Launch type in CreateConstraint and UpdateConstraint APIs" + }, + { + "type": "feature", + "category": "AWS Global Accelerator", + "description": "This update adds an event history to the ListByoipCidr API call. This enables you to see the changes that you've made for an IP address range that you bring to AWS Global Accelerator through bring your own IP address (BYOIP)." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWSKendraFrontendService", + "description": "The Amazon Kendra Microsoft SharePoint data source now supports include and exclude regular expressions and change log features. Include and exclude regular expressions enable you to provide a list of regular expressions to match the display URL of SharePoint documents to either include or exclude documents respectively. When you enable the changelog feature it enables Amazon Kendra to use the SharePoint change log to determine which documents to update in the index." + }, + { + "type": "bugfix", + "category": "AWS SDK for Java v2", + "description": "Remove the `LimitExceededException` as a throttling error as it seems many services don't treat it as a throttling error." + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.5.json b/.changes/2.11.5.json new file mode 100644 index 000000000000..b24f9e69994d --- /dev/null +++ b/.changes/2.11.5.json @@ -0,0 +1,16 @@ +{ + "version": "2.11.5", + "date": "2020-03-30", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "Access Analyzer", + "description": "This release adds support for the creation and management of IAM Access Analyzer analyzers with type organization. An analyzer with type organization continuously monitors all supported resources within the AWS organization and reports findings when they allow access from outside the organization." + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.6.json b/.changes/2.11.6.json new file mode 100644 index 000000000000..41dd8a73c063 --- /dev/null +++ b/.changes/2.11.6.json @@ -0,0 +1,76 @@ +{ + "version": "2.11.6", + "date": "2020-03-31", + "entries": [ + { + "type": "feature", + "category": "AWS Organizations", + "description": "Documentation updates for AWS Organizations" + }, + { + "type": "feature", + "category": "AWS OpsWorks CM", + "description": "Documentation updates for OpsWorks-CM CreateServer values." + }, + { + "type": "feature", + "category": "AWS Glue", + "description": "Add two enums for MongoDB connection: Added \"CONNECTION_URL\" to \"ConnectionPropertyKey\" and added \"MONGODB\" to \"ConnectionType\"" + }, + { + "type": "feature", + "category": "Amazon Elastic Inference", + "description": "This release includes improvements for the Amazon Elastic Inference service." + }, + { + "type": "feature", + "category": "Amazon Detective", + "description": "Removing the notes that Detective is in preview, in preparation for the Detective GA release." + }, + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS WAFV2", + "description": "Added support for AWS Firewall Manager for WAFv2 and PermissionPolicy APIs for WAFv2." + }, + { + "type": "feature", + "category": "Amazon AppConfig", + "description": "This release adds an event log to deployments. In the case of a deployment rollback, the event log details the rollback reason." + }, + { + "type": "feature", + "category": "AWS Elemental MediaStore", + "description": "This release adds support for CloudWatch Metrics. You can now set a policy on your container to dictate which metrics MediaStore sends to CloudWatch." + }, + { + "type": "feature", + "category": "Amazon Rekognition", + "description": "This release adds DeleteProject and DeleteProjectVersion APIs to Amazon Rekognition Custom Labels." + }, + { + "type": "feature", + "category": "AWS Storage Gateway", + "description": "Adding audit logging support for SMB File Shares" + }, + { + "type": "feature", + "category": "Amazon Pinpoint", + "description": "This release of the Amazon Pinpoint API introduces MMS support for SMS messages." + }, + { + "type": "feature", + "category": "AWS Lambda", + "description": "AWS Lambda now supports .NET Core 3.1" + }, + { + "type": "feature", + "category": "Firewall Management Service", + "description": "This release contains FMS wafv2 support." + } + ] +} \ No newline at end of file diff --git a/.changes/2.11.7.json b/.changes/2.11.7.json new file mode 100644 index 000000000000..61f57b57194d --- /dev/null +++ b/.changes/2.11.7.json @@ -0,0 +1,21 @@ +{ + "version": "2.11.7", + "date": "2020-04-01", + "entries": [ + { + "type": "feature", + "category": "AWS SDK for Java v2", + "description": "Updated service endpoint metadata." + }, + { + "type": "feature", + "category": "AWS MediaConnect", + "description": "You can now send content from your virtual private cloud (VPC) to your MediaConnect flow without going over the public internet." + }, + { + "type": "feature", + "category": "AWS IoT", + "description": "This release introduces Dimensions for AWS IoT Device Defender. Dimensions can be used in Security Profiles to collect and monitor fine-grained metrics." + } + ] +} \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 000000000000..b3680fa48f95 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,38 @@ +--- +name: "\U0001F41B Bug report" +about: Create a report to help us improve +labels: bug, needs-triage +--- + + + +## Describe the bug + + +## Expected Behavior + + +## Current Behavior + + + + + + +## Steps to Reproduce + + + + +## Possible Solution + + +## Context + + + +## Your Environment + +* AWS Java SDK version used: +* JDK version used: +* Operating System and version: diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 000000000000..43530d2152da --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,36 @@ +--- +name: "\U0001F680 Feature Request" +about: Suggest an idea for this project +labels: feature-request, needs-triage +--- + + + +## Describe the Feature + + +## Is your Feature Request related to a problem? + + +## Proposed Solution + + +## Describe alternatives you've considered + + +## Additional Context + + + + + + + +- [ ] I may be able to implement this feature request + + +## Your Environment + +* AWS Java SDK version used: +* JDK version used: +* Operating System and version: diff --git a/.github/ISSUE_TEMPLATE/general-issue.md b/.github/ISSUE_TEMPLATE/general-issue.md new file mode 100644 index 000000000000..aad931388a4c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general-issue.md @@ -0,0 +1,28 @@ +--- +name: "\U0001F4AC General Issue" +about: Create a new issue +labels: guidance, needs-triage +--- + + + +## Describe the issue + + +## Steps to Reproduce + + + + +## Current Behavior + + + + + + +## Your Environment + +* AWS Java SDK version used: +* JDK version used: +* Operating System and version: diff --git a/.travis.yml b/.travis.yml index 5682fd599751..cf0b98cddd92 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,12 @@ language: java jdk: - - oraclejdk8 + - openjdk8 - openjdk11 sudo: true -dist: precise +dist: xenial install: /bin/true +env: + - AWS_REGION=us-west-2 notifications: email: - github-awsforjava@amazon.com diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a455d8e52d5..4f30c0afcaf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,2214 @@ +# __2.11.7__ __2020-04-01__ +## __AWS IoT__ + - ### Features + - This release introduces Dimensions for AWS IoT Device Defender. Dimensions can be used in Security Profiles to collect and monitor fine-grained metrics. + +## __AWS MediaConnect__ + - ### Features + - You can now send content from your virtual private cloud (VPC) to your MediaConnect flow without going over the public internet. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +# __2.11.6__ __2020-03-31__ +## __AWS Elemental MediaStore__ + - ### Features + - This release adds support for CloudWatch Metrics. You can now set a policy on your container to dictate which metrics MediaStore sends to CloudWatch. + +## __AWS Glue__ + - ### Features + - Add two enums for MongoDB connection: Added "CONNECTION_URL" to "ConnectionPropertyKey" and added "MONGODB" to "ConnectionType" + +## __AWS Lambda__ + - ### Features + - AWS Lambda now supports .NET Core 3.1 + +## __AWS OpsWorks CM__ + - ### Features + - Documentation updates for OpsWorks-CM CreateServer values. + +## __AWS Organizations__ + - ### Features + - Documentation updates for AWS Organizations + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS Storage Gateway__ + - ### Features + - Adding audit logging support for SMB File Shares + +## __AWS WAFV2__ + - ### Features + - Added support for AWS Firewall Manager for WAFv2 and PermissionPolicy APIs for WAFv2. + +## __Amazon AppConfig__ + - ### Features + - This release adds an event log to deployments. In the case of a deployment rollback, the event log details the rollback reason. + +## __Amazon Detective__ + - ### Features + - Removing the notes that Detective is in preview, in preparation for the Detective GA release. + +## __Amazon Elastic Inference__ + - ### Features + - This release includes improvements for the Amazon Elastic Inference service. + +## __Amazon Pinpoint__ + - ### Features + - This release of the Amazon Pinpoint API introduces MMS support for SMS messages. + +## __Amazon Rekognition__ + - ### Features + - This release adds DeleteProject and DeleteProjectVersion APIs to Amazon Rekognition Custom Labels. + +## __Firewall Management Service__ + - ### Features + - This release contains FMS wafv2 support. + +# __2.11.5__ __2020-03-30__ +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Access Analyzer__ + - ### Features + - This release adds support for the creation and management of IAM Access Analyzer analyzers with type organization. An analyzer with type organization continuously monitors all supported resources within the AWS organization and reports findings when they allow access from outside the organization. + +# __2.11.4__ __2020-03-27__ +## __AWS Global Accelerator__ + - ### Features + - This update adds an event history to the ListByoipCidr API call. This enables you to see the changes that you've made for an IP address range that you bring to AWS Global Accelerator through bring your own IP address (BYOIP). + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + + - ### Bugfixes + - Remove the `LimitExceededException` as a throttling error as it seems many services don't treat it as a throttling error. + +## __AWS Service Catalog__ + - ### Features + - Added "LocalRoleName" as an acceptable Parameter for Launch type in CreateConstraint and UpdateConstraint APIs + +## __AWSKendraFrontendService__ + - ### Features + - The Amazon Kendra Microsoft SharePoint data source now supports include and exclude regular expressions and change log features. Include and exclude regular expressions enable you to provide a list of regular expressions to match the display URL of SharePoint documents to either include or exclude documents respectively. When you enable the changelog feature it enables Amazon Kendra to use the SharePoint change log to determine which documents to update in the index. + +# __2.11.3__ __2020-03-26__ +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS SecurityHub__ + - ### Features + - Security Hub has now made it easier to opt out of default standards when you enable Security Hub. We added a new Boolean parameter to EnableSecurityHub called EnableDefaultStandards. If that parameter is true, Security Hub's default standards are enabled. A new Boolean parameter for standards, EnabledByDefault, indicates whether a standard is a default standard. Today, the only default standard is CIS AWS Foundations Benchmark v1.2. Additional default standards will be added in the future.To learn more, visit our documentation on the EnableSecurityHub API action. + +## __Amazon FSx__ + - ### Features + - This release includes two changes: a new lower-cost, storage type called HDD (Hard Disk Drive), and a new generation of the Single-AZ deployment type called Single AZ 2. The HDD storage type can be selected on Multi AZ 1 and Single AZ 2 deployment types. + +## __Amazon SageMaker Service__ + - ### Features + - This release updates Amazon Augmented AI CreateFlowDefinition API and DescribeFlowDefinition response. + +# __2.11.2__ __2020-03-25__ +## __AWS Cost Explorer Service__ + - ### Features + - Customers can now receive Savings Plans recommendations at the member (linked) account level. + +## __AWS SDK for Java v2__ + - ### Features + - Added a `defaultProfileFile` and `defaultProfileName` option to the client override configuration. Setting this configuration value is equivalent to setting the environment or system properties for the profile file and profile name. Specifically, it sets the default profile file and profile name used by the client. + - Reduced the number of times the profile file configuration is read from disk on client creation from 3-5 to 1. + - Updated service endpoint metadata. + +## __AWS X-Ray__ + - ### Features + - GetTraceSummaries - Now provides additional root cause attribute ClientImpacting which indicates whether root cause impacted trace client. + +## __Amazon CloudWatch Application Insights__ + - ### Features + - Amazon CloudWatch Application Insights for .NET and SQL Server now integrates with Amazon CloudWatch Events (AWS CodeDeploy, AWS Health and Amazon EC2 state changes). This feature enables customers to view events related to problems detected by CloudWatch Application Insights, and reduce mean-time-to-resolution (MTTR). + +## __Amazon Detective__ + - ### Features + - The new ACCEPTED_BUT_DISABLED member account status indicates that a member account that accepted the invitation is blocked from contributing data to the behavior graph. The reason is provided in the new DISABLED_REASON property. The new StartMonitoringMember operation enables a blocked member account. + +## __Amazon DynamoDB__ + - ### Features + - When endpoint discovery is enabled, the endpoint discovery process is now initialized with the first request, instead of 60 seconds after the first request. + + - ### Bugfixes + - Fixed an issue that could cause a null-pointer-exception when using anonymous credentials with endpoint discovery enabled. + - Fixed an issue where endpoint discovery configuration specified in the profile file was being ignored. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Bugfixes + - Performance improvements. + +## __Amazon Elasticsearch Service__ + - ### Features + - Adding support for customer packages (dictionary files) to Amazon Elasticsearch Service + +## __Amazon Managed Blockchain__ + - ### Features + - Amazon Managed Blockchain now has support to publish Hyperledger Fabric peer node, chaincode, and certificate authority (CA) logs to Amazon CloudWatch Logs. + +## __Amazon S3__ + - ### Bugfixes + - Fixed a bug where explicitly disabling use-arn-region on S3Configuration would have lower priority than the environment variable, system property or profile property. + +# __2.11.1__ __2020-03-24__ +## __AWS Organizations__ + - ### Features + - Introduces actions for giving a member account administrative Organizations permissions for an AWS service. You can run this action only for AWS services that support this feature. + +## __AWS RDS DataService__ + - ### Features + - Documentation updates for rds-data + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon Athena__ + - ### Features + - Documentation updates for Athena, including QueryExecutionStatus QUEUED and RUNNING states. QUEUED now indicates that the query has been submitted to the service. RUNNING indicates that the query is in execution phase. + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Adding new error codes: Ec2SubnetInvalidConfiguration and NodeCreationFailure for Nodegroups in EKS + +# __2.11.0__ __2020-03-23__ +## __AWS SDK for Java v2__ + - ### Features + - Bump minor version to '2.11.0-SNAPSHOT' because of [#1692](https://github.com/aws/aws-sdk-java-v2/issues/1692) + - Updating dependency version: netty 4.1.42.Final -> 4.1.46.Final (contains the fix for reducing heap usage for netty client) + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Adding new error code IamLimitExceeded for Nodegroups in EKS + +## __Amazon Route 53__ + - ### Features + - Documentation updates for Route 53. + +## __AmazonApiGatewayV2__ + - ### Features + - Documentation updates to reflect that the default timeout for integrations is now 30 seconds for HTTP APIs. + +# __2.10.91__ __2020-03-20__ +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS Service Catalog__ + - ### Features + - Added "productId" and "portfolioId" to responses from CreateConstraint, UpdateConstraint, ListConstraintsForPortfolio, and DescribeConstraint APIs + +# __2.10.90__ __2020-03-19__ +## __AWS Certificate Manager__ + - ### Features + - AWS Certificate Manager documentation updated on API calls ImportCertificate and ListCertificate. Specific updates included input constraints, private key size for import and next token size for list. + +## __AWS Outposts__ + - ### Features + - Documentation updates for AWS Outposts. + +# __2.10.89__ __2020-03-18__ +## __AWS MediaConnect__ + - ### Features + - Feature adds the ability for a flow to have multiple redundant sources that provides resiliency to a source failing. The new APIs added to enable the feature are, AddFlowSources, RemoveFlowSource and UpdateFlow. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon Personalize__ + - ### Features + - [Personalize] Adds support for returning hyperparameter values of the best performing model in a HPO job. + +## __Amazon Relational Database Service__ + - ### Features + - Updated the MaxRecords type in DescribeExportTasks to Integer. + +# __2.10.88__ __2020-03-17__ +## __AWS Elemental MediaConvert__ + - ### Features + - AWS Elemental MediaConvert SDK has added support for: AV1 encoding in File Group MP4, DASH and CMAF DASH outputs; PCM/WAV audio output in MPEG2-TS containers; and Opus audio in Webm inputs. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + + - ### Bugfixes + - Fix an issue where the signing key is created only once at the start of the request for event streaming requests. This causes requests that span two or more days to have signing errors once the date changes because the signing key was derived only once using the date at the beginning of the request. + +# __2.10.87__ __2020-03-16__ +## __AWS S3 Control__ + - ### Features + - Amazon S3 now supports Batch Operations job tagging. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon Cognito Identity Provider__ + - ### Features + - Additional response field "CompromisedCredentialsDetected" added to AdminListUserAuthEvents. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - The enhanced DDB client table schema now supports custom AttributeConverterProviders, and StaticAttribute can take individual AttributeConverter to override default attribute converter behavior. + +## __Amazon EC2 Container Service__ + - ### Features + - This release adds the ability to update the task placement strategy and constraints for Amazon ECS services. + +## __Amazon ElastiCache__ + - ### Features + - Amazon ElastiCache now supports Global Datastore for Redis. Global Datastore for Redis offers fully managed, fast, reliable and secure cross-region replication. Using Global Datastore for Redis, you can create cross-region read replica clusters for ElastiCache for Redis to enable low-latency reads and disaster recovery across regions. You can create, modify and describe a Global Datastore, as well as add or remove regions from your Global Datastore and promote a region as primary in Global Datastore. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - Resource data sync for AWS Systems Manager Inventory now includes destination data sharing. This feature enables you to synchronize inventory data from multiple AWS accounts into a central Amazon S3 bucket. To use this feature, all AWS accounts must be listed in AWS Organizations. + +# __2.10.86__ __2020-03-13__ +## __Amazon AppConfig__ + - ### Features + - This release adds S3 as a configuration source provider. + +# __2.10.85__ __2020-03-12__ +## __AWS IoT__ + - ### Features + - As part of this release, we are extending capability of AWS IoT Rules Engine to support IoT Cloudwatch log action. The IoT Cloudwatch log rule action lets you send messages from IoT sensors and applications to Cloudwatch logs for troubleshooting and debugging. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS SecurityHub__ + - ### Features + - The AWS Security Finding Format is being augmented with the following changes. 21 new resource types without corresponding details objects are added. Another new resource type, AwsS3Object, has an accompanying details object. Severity.Label is a new string field that indicates the severity of a finding. The available values are: INFORMATIONAL, LOW, MEDIUM, HIGH, CRITICAL. The new string field Workflow.Status indicates the status of the investigation into a finding. The available values are: NEW, NOTIFIED, RESOLVED, SUPPRESSED. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Documentation updates for EC2 + +## __Amazon Lex Model Building Service__ + - ### Features + - Amazon Lex now supports tagging for bots, bot aliases and bot channels. + +## __AmazonApiGatewayV2__ + - ### Features + - Amazon API Gateway HTTP APIs is now generally available. HTTP APIs offer the core functionality of REST API at up to 71% lower price compared to REST API, 60% lower p99 latency, and is significantly easier to use. As part of general availability, we added new features to route requests to private backends such as private ALBs, NLBs, and IP/ports. We also brought over a set of features from REST API such as Stage Variables, and Stage/Route level throttling. Custom domain names can also now be used with both REST And HTTP APIs. + +# __2.10.84__ __2020-03-11__ +## __Amazon Elastic File System__ + - ### Features + - Documentation updates for elasticfilesystem + +## __Amazon Redshift__ + - ### Features + - Amazon Redshift now supports operations to pause and resume a cluster on demand or on a schedule. + +# __2.10.83__ __2020-03-10__ +## __AWS IoT Events__ + - ### Features + - API update that adds a new parameter, durationExpression, to SetTimerAction, and deprecates seconds + +## __AWS Marketplace Commerce Analytics__ + - ### Features + - Change the disbursement data set to look past 31 days instead until the beginning of the month. + +## __AWS SDK for Java v2__ + - ### Bugfixes + - Reverts a recent change from 2.10.70 where the json protocol type was changed to application/json, this is now back to application/x-amz-json-1.1. + +## __AWSServerlessApplicationRepository__ + - ### Features + - AWS Serverless Application Repository now supports sharing applications privately with AWS Organizations. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Documentation updates for EC2 + +## __Amazon Transcribe Service__ + - ### Features + - Amazon Transcribe's Automatic Content Redaction feature enables you to automatically redact sensitive personally identifiable information (PII) from transcription results. It replaces each instance of an identified PII utterance with a [PII] tag in the transcript. + +# __2.10.82__ __2020-03-09__ +## __AWS Database Migration Service__ + - ### Features + - Added new settings for Kinesis target to include detailed transaction info; to capture table DDL details; to use single-line unformatted json, which can be directly queried by AWS Athena if data is streamed into S3 through AWS Kinesis Firehose. Added CdcInsertsAndUpdates in S3 target settings to allow capture ongoing insertions and updates only. + +## __AWS Elemental MediaLive__ + - ### Features + - AWS Elemental MediaLive now supports the ability to configure the Preferred Channel Pipeline for channels contributing to a Multiplex. + +## __AWS SDK for Java v2__ + - ### Features + - Added support for "retry modes". A retry mode allows configuring multiple SDK parameters at once using default retry profiles, some of which are standardized between AWS SDK languages. See RetryMode javadoc for more information. + - Added the ability to configure or disable the default retry throttling behavior of the SDK that 'kicks in' during a large volume of retriable service call errors. This behavior can now be configured via `RetryPolicy.retryCapacityCondition`. + + - ### Bugfixes + - Fixed an issue where specifying your own retry policy would override AWS and service-specific retry conditions. By default, all retry policies now have AWS and service-specific retry conditions added. This can be disabled via the new `RetryPolicy.furtherRefinementsAllowed(false)`. + - Fixed an issue where the retry condition returned by `RetryPolicy.retryCondition` differed from the one specified by `RetryPolicy.Builder.retryCondition`. The old value can be accessed via the new `RetryPolicy.aggregateRetryCondition`. + - Use the last seen HTTP/1.1 header value for headers defined to only appear once in an HTTP message instead of merging them all into a list. The order in which header values are inspected is: headers set by the request marshaller, overridden headers set on the client, then finally overridden headers set on the SDK request object. See https://tools.ietf.org/html/rfc2616#section-4.2 for more information. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Amazon Virtual Private Cloud (VPC) NAT Gateway adds support for tagging on resource creation. + +# __2.10.81__ __2020-03-06__ +## __AWS App Mesh__ + - ### Features + - App Mesh now supports sharing a Mesh with other AWS accounts. Customers can use AWS Resource Access Manager to share their Mesh with other accounts in their organization to connection applications within a single service mesh. See https://docs.aws.amazon.com/app-mesh/latest/userguide/sharing.html for details. + +## __AWS RoboMaker__ + - ### Features + - Added support for streaming a GUI from robot and simulation applications + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS Signer__ + - ### Features + - This release enables signing image format override in PutSigningProfile requests, adding two more enum fields, JSONEmbedded and JSONDetached. This release also extends the length limit of SigningProfile name from 20 to 64. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release provides customers with a self-service option to enable Local Zones. + +## __Amazon GuardDuty__ + - ### Features + - Amazon GuardDuty findings now include the OutpostArn if the finding is generated for an AWS Outposts EC2 host. + +## __Netty NIO Http Client__ + - ### Bugfixes + - Expand Http2 connection-level flow control window when a new stream is acquired on that connection so that the connection-level window size is proportional to the number of streams. + +# __2.10.80__ __2020-03-05__ +## __AWS OpsWorks CM__ + - ### Features + - Updated the Tag regex pattern to align with AWS tagging APIs. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Adds javadoc to operation methods and request/response objects. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - You can now create AWS Client VPN Endpoints with a specified VPC and Security Group. Additionally, you can modify these attributes when modifying the endpoint. + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Amazon EKS now supports adding a KMS key to your cluster for envelope encryption of Kubernetes secrets. + +## __Amazon GuardDuty__ + - ### Features + - Add a new finding field for EC2 findings indicating the instance's local IP address involved in the threat. + +# __2.10.79__ __2020-03-04__ +## __Amazon Pinpoint__ + - ### Features + - This release of the Amazon Pinpoint API introduces support for integrating recommender models with email, push notification, and SMS message templates. You can now use these types of templates to connect to recommender models and add personalized recommendations to messages that you send from campaigns and journeys. + +# __2.10.78__ __2020-03-03__ +## __Amazon Elastic Compute Cloud__ + - ### Features + - Amazon VPC Flow Logs adds support for tags and tagging on resource creation. + +## __Netty NIO HTTP Client__ + - ### Bugfixes + - Fix an issue where the Netty client was prematurely considering an HTTP/2 request body as sent, but was still in the process of being transferred to the remote endpoint. + +# __2.10.77__ __2020-03-02__ +## __AWS Comprehend Medical__ + - ### Features + - New Time Expression feature, part of DetectEntitiesV2 API will provide temporal relations to existing NERe entities such as Medication, Test, Treatment, Procedure and Medical conditions. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon CloudWatch__ + - ### Features + - Introducing Amazon CloudWatch Composite Alarms + +# __2.10.76__ __2020-02-28__ +## __AWS Config__ + - ### Features + - Correcting list of supported resource types. + +# __2.10.75__ __2020-02-28__ +## __AWS App Mesh__ + - ### Features + - App Mesh now supports Transport Layer Security (TLS) between Virtual Nodes in a Mesh. Customers can use managed certificates from an AWS Certificate Manager Private Certificate Authority or bring their own certificates from the local file system to encrypt traffic between their workloads. See https://docs.aws.amazon.com/app-mesh/latest/userguide/virtual-node-tls.html for details. + +## __AWS Config__ + - ### Features + - Accepts a structured query language (SQL) SELECT command and an aggregator name, performs the corresponding search on resources aggregated by the aggregator, and returns resource configurations matching the properties. + +## __AWS Glue__ + - ### Features + - AWS Glue adds resource tagging support for Machine Learning Transforms and adds a new API, ListMLTransforms to support tag filtering. With this feature, customers can use tags in AWS Glue to organize and control access to Machine Learning Transforms. + +## __Access Analyzer__ + - ### Features + - This release includes improvements and fixes bugs for the IAM Access Analyzer feature. + +## __Amazon Augmented AI Runtime__ + - ### Features + - This release updates Amazon Augmented AI ListHumanLoops API, DescribeHumanLoop response, StartHumanLoop response and type names of SDK fields. + +## __Amazon CodeGuru Profiler__ + - ### Features + - Documentation updates for Amazon CodeGuru Profiler + +## __Amazon QuickSight__ + - ### Features + - Added SearchDashboards API that allows listing of dashboards that a specific user has access to. + +## __Amazon WorkDocs__ + - ### Features + - Documentation updates for workdocs + +## __Elastic Load Balancing__ + - ### Features + - Added a target group attribute to support sticky sessions for Network Load Balancers. + +# __2.10.74__ __2020-02-27__ +## __AWS Global Accelerator__ + - ### Features + - This release adds support for adding tags to accelerators and bringing your own IP address to AWS Global Accelerator (BYOIP). + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon Lightsail__ + - ### Features + - Adds support to create notification contacts in Amazon Lightsail, and to create instance, database, and load balancer metric alarms that notify you based on the value of a metric relative to a threshold that you specify. + +# __2.10.73__ __2020-02-26__ +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS SecurityHub__ + - ### Features + - Security Hub has added to the DescribeProducts API operation a new response field called IntegrationTypes. The IntegrationTypes field lists the types of actions that a product performs relative to Security Hub such as send findings to Security Hub and receive findings from Security Hub. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Added the BeanTableSchema implementation of TableSchema that allows a TableSchema to be instantiated from an annotated Java bean class which can then be used with the DynamoDB Enhanced Client. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release changes the RunInstances CLI and SDK's so that if you do not specify a client token, a randomly generated token is used for the request to ensure idempotency. + +## __Amazon SageMaker Service__ + - ### Features + - SageMaker UpdateEndpoint API now supports retained variant properties, e.g., instance count, variant weight. SageMaker ListTrials API filter by TrialComponentName. Make ExperimentConfig name length limits consistent with CreateExperiment, CreateTrial, and CreateTrialComponent APIs. + +## __Amazon Transcribe Service__ + - ### Features + - Amazon Transcribe's Automatic Content Redaction feature enables you to automatically redact sensitive personally identifiable information (PII) from transcription results. It replaces each instance of an identified PII utterance with a [PII] tag in the transcript. + +# __2.10.72__ __2020-02-25__ +## __AWS Outposts__ + - ### Features + - This release adds DeleteSite and DeleteOutpost. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS Secrets Manager__ + - ### Features + - This release increases the maximum allowed size of SecretString or SecretBinary from 10KB to 64KB in the CreateSecret, UpdateSecret, PutSecretValue and GetSecretValue APIs. + +## __AWS Step Functions__ + - ### Features + - This release adds support for CloudWatch Logs for Standard Workflows. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Improves discoverability by adding consumer-style methods for all client, table and index operations. + +## __Managed Streaming for Kafka__ + - ### Features + - Amazon MSK has added support for Broker Log delivery to CloudWatch, S3, and Firehose. + +# __2.10.71__ __2020-02-24__ +## __AWS IoT Events__ + - ### Features + - Documentation updates for iotcolumbo + +## __Amazon CloudWatch Events__ + - ### Features + - This release allows you to create and manage tags for event buses. + +## __Amazon DocumentDB with MongoDB compatibility__ + - ### Features + - Documentation updates for docdb + +## __Amazon EventBridge__ + - ### Features + - This release allows you to create and manage tags for event buses. + +## __Amazon FSx__ + - ### Features + - Announcing persistent file systems for Amazon FSx for Lustre that are ideal for longer-term storage and workloads, and a new generation of scratch file systems that offer higher burst throughput for spiky workloads. + +## __Amazon Import/Export Snowball__ + - ### Features + - AWS Snowball adds a field for entering your GSTIN when creating AWS Snowball jobs in the Asia Pacific (Mumbai) region. + +# __2.10.70__ __2020-02-21__ +## __AWS WAFV2__ + - ### Features + - Documentation updates for AWS WAF (wafv2) to correct the guidance for associating a web ACL to a CloudFront distribution. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Improves discoverability by adding consumer-style methods for all client, table and index operations. + +## __Amazon Redshift__ + - ### Features + - Extend elastic resize to support resizing clusters to different instance types. + +## __EC2 Image Builder__ + - ### Features + - This release of EC2 Image Builder increases the maximum policy document size for Image Builder resource-based policy APIs. + +# __2.10.69__ __2020-02-20__ +## __AWS Savings Plans__ + - ### Features + - Added support for AWS Lambda in Compute Savings Plans + +## __Amazon AppConfig__ + - ### Features + - This release adds exponential growth type support for deployment strategies. + +## __Amazon Pinpoint__ + - ### Features + - As of this release of the Amazon Pinpoint API, the Title property is optional for the CampaignEmailMessage object. + +# __2.10.68__ __2020-02-19__ +## __AWS Lambda__ + - ### Features + - AWS Lambda now supports Ruby 2.7 + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS Service Catalog__ + - ### Features + - "ListPortfolioAccess" API now has a new optional parameter "OrganizationParentId". When it is provided and if the portfolio with the "PortfolioId" given was shared with an organization or organizational unit with "OrganizationParentId", all accounts in the organization sub-tree under parent which inherit an organizational portfolio share will be listed, rather than all accounts with external shares. To accommodate long lists returned from the new option, the API now supports pagination. + +## __Auto Scaling__ + - ### Features + - Doc update for EC2 Auto Scaling: Add Enabled parameter for PutScalingPolicy + +# __2.10.67__ __2020-02-18__ +## __Amazon Chime__ + - ### Features + - Added AudioFallbackUrl to support Chime SDK client. + +## __Amazon Relational Database Service__ + - ### Features + - This release supports Microsoft Active Directory authentication for Amazon Aurora. + +## __Auto Scaling__ + - ### Features + - Amazon EC2 Auto Scaling now supports the ability to enable/disable target tracking, step scaling, and simple scaling policies. + +# __2.10.66__ __2020-02-17__ +## __AWS Cloud9__ + - ### Features + - AWS Cloud9 now supports the ability to tag Cloud9 development environments. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon DynamoDB__ + - ### Features + - Amazon DynamoDB enables you to restore your DynamoDB backup or table data across AWS Regions such that the restored table is created in a different AWS Region from where the source table or backup resides. You can do cross-region restores between AWS commercial Regions, AWS China Regions, and AWS GovCloud (US) Regions. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Documentation updates for EC2 + +## __Amazon Rekognition__ + - ### Features + - This update adds the ability to detect text in videos and adds filters to image and video text detection. + +# __2.10.65__ __2020-02-14__ +## __AWS MediaTailor__ + - ### Features + - AWS Elemental MediaTailor SDK now allows configuration of Personalization Threshold for HLS and DASH streams. + +## __AWS SecurityHub__ + - ### Features + - Security Hub has released a new DescribeStandards API action. This API action allows a customer to list all of the standards available in an account. For each standard, the list provides the customer with the standard name, description, and ARN. Customers can use the ARN as an input to the BatchEnableStandards API action. To learn more, visit our API documentation. + +## __AWS Shield__ + - ### Features + - This release adds support for associating Amazon Route 53 health checks to AWS Shield Advanced protected resources. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - You can now enable Multi-Attach on Provisioned IOPS io1 volumes through the create-volume API. + +## __Amazon S3__ + - ### Features + - Added support for presigning `CreateMultipartUpload`, `UploadPart`, `CompleteMultipartUpload`, and `AbortMultipartUpload` requests. + +# __2.10.64__ __2020-02-13__ +## __AWS Elemental MediaPackage VOD__ + - ### Features + - Adds support for DASH with multiple media presentation description periods triggered by presence of SCTE-35 ad markers in the manifest.Also adds optional configuration for DASH SegmentTemplateFormat to refer to segments by Number with Duration, Number with Timeline or Time with Timeline and compact the manifest by combining duplicate SegmentTemplate tags. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Improves discoverability by adding operation methods for deleteItem(), getItem(), putItem and updateItem(), as applicable. These methods take a request object as parameter. Execute() methods for the table interface is removed since they are no longer needed. + +## __Netty NIO HTTP Client__ + - ### Features + - When there is an I/O error on an http2 request, the SDK will start shutting down the connection - stopping using the http2 connection for new requests and closing it after all streams are finished. + +# __2.10.63__ __2020-02-12__ +## __AWS Directory Service__ + - ### Features + - Release to add the ExpirationDateTime as an output to ListCertificates so as to ease customers to look into their certificate lifetime and make timely decisions about renewing them. + +## __AWS Glue__ + - ### Features + - Adding ability to add arguments that cannot be overridden to AWS Glue jobs + +## __Amazon Chime__ + - ### Features + - Documentation updates for Amazon Chime + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds support for tagging public IPv4 pools. + +## __Amazon Elasticsearch Service__ + - ### Features + - Amazon Elasticsearch Service now offers fine-grained access control, which adds multiple capabilities to give tighter control over data. New features include the ability to use roles to define granular permissions for indices, documents, or fields and to extend Kibana with read-only views and secure multi-tenant support. + +## __Amazon Neptune__ + - ### Features + - This launch enables Neptune start-db-cluster and stop-db-cluster. Stopping and starting Amazon Neptune clusters helps you manage costs for development and test environments. You can temporarily stop all the DB instances in your cluster, instead of setting up and tearing down all the DB instances each time that you use the cluster. + +## __Amazon WorkMail__ + - ### Features + - This release adds support for access control rules management in Amazon WorkMail. + +# __2.10.62__ __2020-02-11__ +## __AWS CloudFormation__ + - ### Features + - This release of AWS CloudFormation StackSets allows you to centrally manage deployments to all the accounts in your organization or specific organizational units (OUs) in AWS Organizations. You will also be able to enable automatic deployments to any new accounts added to your organization or OUs. The permissions needed to deploy across accounts will automatically be taken care of by the StackSets service. + +## __Amazon Cognito Identity Provider__ + - ### Features + - Features:This release adds a new setting for a user pool to allow if customer wants their user signup/signin with case insensitive username. The current default setting is case sensitive, and for our next release we will change it to case insensitive. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Amazon EC2 Now Supports Tagging Spot Fleet. + +# __2.10.61__ __2020-02-10__ +## __AWS Key Management Service__ + - ### Features + - The ConnectCustomKeyStore API now provides a new error code (SUBNET_NOT_FOUND) for customers to better troubleshoot if their "connect-custom-key-store" operation fails. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon DocumentDB with MongoDB compatibility__ + - ### Features + - Added clarifying information that Amazon DocumentDB shares operational technology with Amazon RDS and Amazon Neptune. + +# __2.10.60__ __2020-02-07__ +## __AWS RoboMaker__ + - ### Features + - This release adds support for simulation job batches + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Improves discoverability by renaming the table and index interfaces to be consistent with the client interface naming, and by adding operation methods for createTable(), scan() and query(), as applicable. These methods take a request object as parameter. Execute() methods for the index interface is removed since they are no longer needed. + +## __Amazon Relational Database Service__ + - ### Features + - Documentation updates for RDS: when restoring a DB cluster from a snapshot, must create DB instances + +## __EC2 Image Builder__ + - ### Features + - This version of the SDK includes bug fixes and documentation updates. + +# __2.10.59__ __2020-02-06__ +## __AWS AppSync__ + - ### Features + - AWS AppSync now supports X-Ray + +## __AWS CodeBuild__ + - ### Features + - AWS CodeBuild adds support for Amazon Elastic File Systems + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - In order to make operations more easily discoverable by an IDE, specific operation methods have been added to the enhanced client interface. An operation method takes a corresponding request object as parameter. Meanwhile, the generic execute() method is removed. This change affects only batch and transcribe operations at the database level. + +## __Amazon EC2 Container Registry__ + - ### Features + - This release contains updated text for the GetAuthorizationToken API. + +## __Amazon Elastic Block Store__ + - ### Features + - Documentation updates for EBS direct APIs. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds platform details and billing info to the DescribeImages API. + +## __Amazon Lex Model Building Service__ + - ### Features + - Amazon Lex now supports AMAZON.AlphaNumeric with regular expressions. + +# __2.10.58__ __2020-02-05__ +## __AWS Elemental MediaConvert__ + - ### Features + - AWS Elemental MediaConvert SDK has added support for fine-tuned QVBR quality level. + +## __AWS Ground Station__ + - ### Features + - Adds dataflowEndpointRegion property to DataflowEndpointConfig. The dateCreated, lastUpdated, and tags properties on GetSatellite have been deprecated. + +## __AWS Resource Groups Tagging API__ + - ### Features + - Documentation-only update that adds services to the list of supported services. + +## __AWS SecurityHub__ + - ### Features + - Additional resource types are now supported in the AWS Security Finding Format (ASFF). The following new resource types are added, each having an accompanying resource details object with fields for security finding providers to populate: AwsCodeBuildProject, AwsEc2NetworkInterface, AwsEc2SecurityGroup, AwsElasticsearchDomain, AwsLambdaLayerVersion, AwsRdsDbInstance, and AwsWafWebAcl. The following resource types are added without an accompanying details object: AutoscalingAutoscalingGroup, AwsDynamoDbTable, AwsEc2Eip, AwsEc2Snapshot, AwsEc2Volume, AwsRdsDbSnapshot, AwsRedshiftCluster, and AwsS3Object. The number of allowed resources per finding is increased from 10 to 32. A new field is added in the Compliance object, RelatedRequirements. To learn more, visit our documentation on the ASFF. + +## __Amazon Data Lifecycle Manager__ + - ### Features + - Updated the maximum number of tags that can be added to a snapshot using DLM to 45. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release provides support for tagging when you create a VPC endpoint, or VPC endpoint service. + +## __Amazon Forecast Query Service__ + - ### Features + - Documentation updates for Amazon Forecast. + +# __2.10.57__ __2020-02-04__ +## __AWS IoT__ + - ### Features + - Updated ThrottlingException documentation to report that the error code is 400, and not 429, to reflect actual system behaviour. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS Storage Gateway__ + - ### Features + - Adding KVM as a support hypervisor + +## __Amazon CloudFront__ + - ### Features + - Documentation updates for CloudFront + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Changing usage of typed builders for PutItem, UpdateItem and StaticTableSchema to explicitly provide class type. + - Renames top level sync/async MappedDatabase interfaces as DynamoDbEnhancedClient interfaces. Also adds builder definitions to the interfaces together with a static method that returns the default implementation of the builder. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Amazon VPC Flow Logs adds support for 1-minute aggregation intervals. + +## __Amazon S3__ + - ### Bugfixes + - Fixed an issue where fields in `ListObjectVersionsResponse` and `ListMultipartUploadsResponse` are not decoded correctly when encodingType is specified as url. See [#1601](https://github.com/aws/aws-sdk-java-v2/issues/1601) + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - This feature ensures that an instance is patched up to the available patches on a particular date. It can be enabled by selecting the 'ApproveUntilDate' option as the auto-approval rule while creating the patch baseline. ApproveUntilDate - The cutoff date for auto approval of released patches. Any patches released on or before this date will be installed automatically. + +## __Amazon WorkMail__ + - ### Features + - This release adds support for tagging Amazon WorkMail organizations. + +## __Managed Streaming for Kafka__ + - ### Features + - This release enables AWS MSK customers to list Apache Kafka versions that are supported on AWS MSK clusters. Also includes changes to expose additional details of a cluster's state in DescribeCluster and ListClusters APIs. + +## __Netty NIO HTTP Client__ + - ### Bugfixes + - Deliver exceptions to stream channels correctly if there's an exception thrown on connection. This also fixes a bug where publisher signals onComplete if the stream is closed as a result of outbound GOAWAY. + - Throws `IOException` for the race condition where an HTTP2 connection gets reused at the same time it gets inactive so that failed requests can be retried + +# __2.10.56__ __2020-01-24__ +## __AWS DataSync__ + - ### Features + - AWS DataSync now supports FSx for Windows File Server Locations + +## __AWS OpsWorks CM__ + - ### Features + - AWS OpsWorks for Chef Automate now supports in-place upgrade to Chef Automate 2. Eligible servers can be updated through the management console, CLI and APIs. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon EC2__ + - ### Features + - Adds EC2ThrottledException as a recognized throttling exception to be retried + +## __Amazon EC2 Container Service__ + - ### Features + - This release provides support for tagging Amazon ECS task sets for services using external deployment controllers. + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Adding new error codes for Nodegroups in EKS + +## __Amazon WorkSpaces__ + - ### Features + - Documentation updates for WorkSpaces + +## __Netty NIO HTTP Client__ + - ### Bugfixes + - Fix issue where DNS resolution for a host is only made once for the initial request to the host. If the DNS entries change for a hostname, the client will resolve the new address until the client is closed and recreated. + +# __2.10.55__ __2020-01-23__ +## __AWS Identity and Access Management__ + - ### Features + - This release enables the Identity and Access Management policy simulator to simulate permissions boundary policies. + +## __AWS SDK for Java v2__ + - ### Features + - Added ServiceMetadata.servicePartitions() to get partition metadata for a specific service + - Improved error messages on UnknownHostExceptions + - Updated service endpoint metadata. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Support for non-blocking asynchronous calling of all mapper operations + +## __Amazon Relational Database Service__ + - ### Features + - This SDK release introduces APIs that automate the export of Amazon RDS snapshot data to Amazon S3. The new APIs include: StartExportTask, CancelExportTask, DescribeExportTasks. These APIs automate the extraction of data from an RDS snapshot and export it to an Amazon S3 bucket. The data is stored in a compressed, consistent, and query-able format. After the data is exported, you can query it directly using tools such as Amazon Athena or Redshift Spectrum. You can also consume the data as part of a data lake solution. If you archive the data in S3 Infrequent Access or Glacier, you can reduce long term data storage costs by applying data lifecycle policies. + +# __2.10.54__ __2020-01-21__ +## __AWS Application Discovery Service__ + - ### Features + - Documentation updates for the AWS Application Discovery Service. + +## __AWS CodePipeline__ + - ### Features + - AWS CodePipeline enables an ability to stop pipeline executions. + +## __AWS IoT Events__ + - ### Features + - Documentation updates for iotcolumbo + +## __AWS Marketplace Commerce Analytics__ + - ### Features + - Remove 4 deprecated data sets, change some data sets available dates to 2017-09-15 + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Add an enum value to the result of DescribeByoipCidrs to support CIDRs that are not publicly advertisable. + +## __Netty NIO Http Client__ + - ### Bugfixes + - Fixed a bug where an inactive http2 connection without `GOAWAY` frame received might get reused in a new request, causing `ClosedChannelException` + +# __2.10.53__ __2020-01-20__ +## __AWS Key Management Service__ + - ### Features + - The ConnectCustomKeyStore operation now provides new error codes (USER_LOGGED_IN and USER_NOT_FOUND) for customers to better troubleshoot if their connect custom key store operation fails. Password length validation during CreateCustomKeyStore now also occurs on the client side. + +## __AWS Lambda__ + - ### Features + - Added reason codes to StateReasonCode (InvalidSubnet, InvalidSecurityGroup) and LastUpdateStatusReasonCode (SubnetOutOfIPAddresses, InvalidSubnet, InvalidSecurityGroup) for functions that connect to a VPC. + +## __Alexa For Business__ + - ### Features + - Add support for CreatedTime and ConnectionStatusUpdatedTime in response of SearchDevices API. + +## __Amazon CloudWatch__ + - ### Features + - Updating DescribeAnomalyDetectors API to return AnomalyDetector Status value in response. + +## __Amazon CloudWatch Application Insights__ + - ### Features + - This release adds support for a list API to retrieve the configuration events logged during periodic updates to an application by Amazon CloudWatch Application Insights. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release provides support for a preview of bringing your own IPv6 addresses (BYOIP for IPv6) for use in AWS. + +# __2.10.52__ __2020-01-17__ +## __AWS Batch__ + - ### Features + - This release ensures INACTIVE job definitions are permanently deleted after 180 days. + +## __AWS CloudHSM V2__ + - ### Features + - This release introduces resource-level and tag-based access control for AWS CloudHSM resources. You can now tag CloudHSM backups, tag CloudHSM clusters on creation, and tag a backup as you copy it to another region. + +## __AWS Elemental MediaConvert__ + - ### Features + - AWS Elemental MediaConvert SDK has added support for MP3 audio only outputs. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon EC2 Container Service__ + - ### Features + - This release provides a public preview for specifying Amazon EFS file systems as volumes in your Amazon ECS task definitions. + +## __Amazon Neptune__ + - ### Features + - This release includes Deletion Protection for Amazon Neptune databases. + +## __Amazon Redshift__ + - ### Features + - Documentation updates for redshift + +# __2.10.51__ __2020-01-16__ +## __AWS Directory Service__ + - ### Features + - To reduce the number of errors our customers are facing, we have modified the requirements of input parameters for two of Directory Service APIs. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Client VPN now supports Port Configuration for VPN Endpoints, allowing usage of either port 443 or port 1194. + +## __Amazon SageMaker Service__ + - ### Features + - This release adds two new APIs (UpdateWorkforce and DescribeWorkforce) to SageMaker Ground Truth service for workforce IP whitelisting. + +# __2.10.50__ __2020-01-15__ +## __AWS Organizations__ + - ### Features + - Updated description for PolicyID parameter and ConstraintViolationException. + +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + +## __AWS SecurityHub__ + - ### Features + - Add support for DescribeStandardsControls and UpdateStandardsControl. These new Security Hub API operations are used to track and manage whether a compliance standards control is enabled. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - General Update to EC2 Docs and SDKs + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - Document updates for Patch Manager 'NoReboot' feature. + +## __Amazon Transcribe Service__ + - ### Bugfixes + - Fixed an issue where streaming transcriptions would fail with signature validation errors if the date changed during the request. + +# __2.10.49__ __2020-01-14__ +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds support for partition placement groups and instance metadata option in Launch Templates + +# __2.10.48__ __2020-01-13__ +## __AWS Backup__ + - ### Features + - Cross-region backup is a new AWS Backup feature that allows enterprises to copy backups across multiple AWS services to different regions. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Documentation updates for the StopInstances API. You can now stop and start an Amazon EBS-backed Spot Instance at will, instead of relying on the Stop interruption behavior to stop your Spot Instances when interrupted. + +## __Amazon Elastic File System__ + - ### Features + - This release adds support for managing EFS file system policies and EFS Access Points. + +## __Amazon S3__ + - ### Bugfixes + - Fixed bug prevent GetBucketBolicy from ever being successful using the asynchronous S3 client. + +# __2.10.47__ __2020-01-10__ +## __AWS SDK for Java v2__ + - ### Features + - Updated service endpoint metadata. + - Updated service endpoints and added global endpoints for iso and iso-b. + +## __AWS Transfer for SFTP__ + - ### Features + - This release introduces a new endpoint type that allows you to attach Elastic IP addresses from your AWS account with your server's endpoint directly and whitelist access to your server by client's internet IP address(es) using VPC Security Groups. + +## __Amazon Chime__ + - ### Features + - Add shared profile support to new and existing users + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release introduces the ability to tag egress only internet gateways, local gateways, local gateway route tables, local gateway virtual interfaces, local gateway virtual interface groups, local gateway route table VPC association and local gateway route table virtual interface group association. You can use tags to organize and identify your resources for cost allocation. + +## __Amazon Relational Database Service__ + - ### Features + - This release adds an operation that enables users to override the system-default SSL/TLS certificate for new Amazon RDS DB instances temporarily, or remove the customer override. + +## __Amazon S3__ + - ### Bugfixes + - Fix an issue where s3#listObjects incorrectly decoded marker field. See [#1574](https://github.com/aws/aws-sdk-java-v2/issues/1574). + +## __Amazon SageMaker Service__ + - ### Features + - SageMaker ListTrialComponents API filter by TrialName and ExperimentName. + +## __Amazon WorkSpaces__ + - ### Features + - Added the migrate feature to Amazon WorkSpaces. + +# __2.10.46__ __2020-01-09__ +## __AWS SDK for Java v2__ + - ### Bugfixes + - Increase the priority of the AWS_WEB_IDENTITY_TOKEN_FILE/AWS_ROLE_ARN/AWS_ROLE_SESSION_NAME environment variables when loading credentials so that they are considered before web_identity_token_file/role_arn/role_session_name profile properties. This is consistent with the other AWS SDKs, including the CLI. + +## __AWS Security Token Service__ + - ### Features + - Documentation updates for sts + +## __Amazon CloudWatch Logs__ + - ### Features + - Documentation updates for logs + +## __Amazon S3__ + - ### Features + - Add support for Tagging builder in `CreateMultipartUploadRequest`. See [#1440](https://github.com/aws/aws-sdk-java-v2/issues/1440) + +# __2.10.45__ __2020-01-08__ +## __AWS Cost Explorer Service__ + - ### Features + - Documentation updates for CreateCostCategoryDefinition and UpdateCostCategoryDefinition API + +## __AWS Step Functions__ + - ### Features + - Add sfn specific http configurations. See [#1325](https://github.com/aws/aws-sdk-java-v2/issues/1325) + +## __Amazon EC2__ + - ### Bugfixes + - Fix NPE when calling `CopySnapshot`. Fixes [#1564](https://github.com/aws/aws-sdk-java-v2/issues/1564) + +## __Amazon Translate__ + - ### Features + - This release adds a new family of APIs for asynchronous batch translation service that provides option to translate large collection of text or HTML documents stored in Amazon S3 folder. This service accepts a batch of up to 5 GB in size per API call with each document not exceeding 1 MB size and the number of documents not exceeding 1 million per batch. See documentation for more information. + +## __Firewall Management Service__ + - ### Features + - AWS Firewall Manager now supports tagging, and tag-based access control, of policies. + +# __2.10.44__ __2020-01-07__ +## __AWS CodeBuild__ + - ### Features + - Add encryption key override to StartBuild API in AWS CodeBuild. + +## __AWS Migration Hub__ + - ### Features + - ListApplicationStates API provides a list of all application migration states + +## __AWS X-Ray__ + - ### Features + - Documentation updates for xray + +# __2.10.43__ __2020-01-06__ +## __AWS Elemental MediaPackage__ + - ### Features + - You can now restrict direct access to AWS Elemental MediaPackage by securing requests for live content using CDN authorization. With CDN authorization, content requests require a specific HTTP header and authorization code. + +## __AWS SDK for Java v2__ + - ### Features + - Add `RequestBody.fromRemainingByteBuffer(ByteBuffer)` that copies only the remaining readable bytes of the buffer. See [#1534](https://github.com/aws/aws-sdk-java-v2/issues/1534) + + - ### Bugfixes + - Reduce ReadTimeout and ConnectTimeout for accessing EC2 metadata instance service + +## __Amazon Comprehend__ + - ### Features + - Amazon Comprehend now supports Multilabel document classification + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release supports service providers configuring a private DNS name for services other than AWS services and services available in the AWS marketplace. This feature allows consumers to access the service using an existing DNS name without making changes to their applications. + +## __Amazon S3__ + - ### Bugfixes + - Requests that return an error response in the body of the HTTP response with a successful (200) status code will now correctly be handled as a failed request by the SDK. + +# __2.10.42__ __2020-01-02__ +## __AWS Cost Explorer Service__ + - ### Features + - Documentation updates for GetReservationUtilization for the Cost Explorer API. + +## __AWS SDK for Java v2__ + - ### Bugfixes + - Fix unmarshalling for models with xml attributes. See [#1488](https://github.com/aws/aws-sdk-java-v2/issues/1488). + +## __Amazon EC2 Container Registry__ + - ### Features + - Adds waiters for ImageScanComplete and LifecyclePolicyPreviewComplete + +## __Amazon Lex Model Building Service__ + - ### Features + - Documentation updates for Amazon Lex. + +## __Amazon Lightsail__ + - ### Features + - This release adds support for Certificate Authority (CA) certificate identifier to managed databases in Amazon Lightsail. + +## __Netty NIO Http Client__ + - ### Bugfixes + - Propagate exception properly when an exception is thrown from protocol initialization. + +# __2.10.41__ __2019-12-23__ +## __AWS Health APIs and Notifications__ + - ### Features + - With this release, you can now centrally aggregate AWS Health events from all accounts in your AWS organization. Visit AWS Health documentation to learn more about enabling and using this feature: https://docs.aws.amazon.com/health/latest/ug/organizational-view-health.html. + +## __Amazon Detective__ + - ### Features + - Updated the documentation for Amazon Detective. + +## __Amazon FSx__ + - ### Features + - This release adds a new family of APIs (create-data-repository-task, describe-data-repository-task, and cancel-data-repository-task) that allow users to perform operations between their file system and its linked data repository. + +# __2.10.40__ __2019-12-20__ +## __AWS Device Farm__ + - ### Features + - Introduced browser testing support through AWS Device Farm + +## __AWS SecurityHub__ + - ### Features + - Additional resource types are now fully supported in the AWS Security Finding Format (ASFF). These resources include AwsElbv2LoadBalancer, AwsKmsKey, AwsIamRole, AwsSqsQueue, AwsLambdaFunction, AwsSnsTopic, and AwsCloudFrontDistribution. Each of these resource types includes an accompanying resource details object with fields for security finding providers to populate. Updates were made to the AwsIamAccessKey resource details object to include information on principal ID and name. To learn more, visit our documentation on the ASFF. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release introduces the ability to tag key pairs, placement groups, export tasks, import image tasks, import snapshot tasks and export image tasks. You can use tags to organize and identify your resources for cost allocation. + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Amazon EKS now supports restricting access to the API server public endpoint by applying CIDR blocks + +## __Amazon Pinpoint__ + - ### Features + - This release of the Amazon Pinpoint API introduces versioning support for message templates. + +## __Amazon Redshift__ + - ### Features + - Documentation updates for Amazon Redshift RA3 node types. + +## __Amazon Relational Database Service__ + - ### Features + - This release adds an operation that enables users to specify whether a database is restarted when its SSL/TLS certificate is rotated. Only customers who do not use SSL/TLS should use this operation. + +## __Amazon S3__ + - ### Bugfixes + - Fixed an issue where the SDK would attempt to validate the checksum on a PutObjectRequest when S3 was returning invalid checksums. This would cause all requests to buckets with customer-managed-key service-side encryption to fail. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - This release updates the attachments support to include AttachmentReference source for Automation documents. + +## __Amazon Transcribe Service__ + - ### Features + - AWS Transcribe now supports vocabulary filtering that allows customers to input words to the service that they don't want to see in the output transcript. + +# __2.10.39__ __2019-12-19__ +## __AWS CodeStar connections__ + - ### Features + - Public beta for Bitbucket Cloud support in AWS CodePipeline through integration with AWS CodeStar connections. + +## __Amazon Data Lifecycle Manager__ + - ### Features + - You can now copy snapshots across regions using Data Lifecycle Manager (DLM). You can enable policies which, along with create, can now also copy snapshots to one or more AWS region(s). Copies can be scheduled for up to three regions from a single policy and retention periods are set for each region separately. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - We are updating the supportedRootDevices field to supportedRootDeviceTypes for DescribeInstanceTypes API to ensure that the actual value is returned, correcting a previous error in the model. + +## __Amazon GameLift__ + - ### Features + - Amazon GameLift now supports ARNs for all key GameLift resources, tagging for GameLift resource authorization management, and updated documentation that articulates GameLift's resource authorization strategy. + +## __Amazon Lex Model Building Service__ + - ### Features + - Amazon Lex now supports conversation logs and slot obfuscation. + +## __Amazon Personalize Runtime__ + - ### Features + - Add context map to get-recommendations and get-personalized-ranking request objects to provide contextual metadata at inference time + +## __Amazon S3__ + - ### Bugfixes + - Fixed an issue where a 'checksum mismatch' error is raised whenever a PutObject request is retried while using an async client. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - This release allows customers to add tags to Automation execution, enabling them to sort and filter executions in different ways, such as by resource, purpose, owner, or environment. + +## __Amazon Transcribe Service__ + - ### Features + - Amazon Transcribe supports job queuing for the StartTranscriptionJob API. + +## __Netty NIO HTTP Client__ + - ### Features + - `SETTINGS_INITIAL_WINDOW_SIZE` is now configurable on HTTP/2 connections opened by the Netty client using `Http2Configuration#initialWindowSize(Integer)` along with `NettyNioAsyncHttpClient.Builder#http2Configuration(Http2Configuration)`. See https://tools.ietf.org/html/rfc7540#section-6.5.2 for more information. + +# __2.10.38__ __2019-12-18__ +## __AWS OpsWorks CM__ + - ### Features + - AWS OpsWorks CM now supports tagging, and tag-based access control, of servers and backups. + +## __AWS Resource Groups Tagging API__ + - ### Features + - Documentation updates for resourcegroupstaggingapi + +## __Amazon CloudFront__ + - ### Features + - Documentation updates for CloudFront + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release introduces the ability to tag Elastic Graphics accelerators. You can use tags to organize and identify your accelerators for cost allocation. + +## __Amazon Simple Storage Service__ + - ### Features + - Updates Amazon S3 endpoints allowing you to configure your client to opt-in to using S3 with the us-east-1 regional endpoint, instead of global. + +# __2.10.37__ __2019-12-17__ +## __AWS Elemental MediaLive__ + - ### Features + - AWS Elemental MediaLive now supports HLS ID3 segment tagging, HLS redundant manifests for CDNs that support different publishing/viewing endpoints, fragmented MP4 (fMP4), and frame capture intervals specified in milliseconds. + +## __AWS IoT__ + - ### Features + - Added a new Over-the-Air (OTA) Update feature that allows you to use different, or multiple, protocols to transfer an image from the AWS cloud to IoT devices. + +## __Amazon EC2 Container Service__ + - ### Features + - Documentation updates for Amazon ECS. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Documentation updates for Amazon EC2 + +## __Amazon Kinesis Analytics__ + - ### Features + - Kinesis Data Analytics service now supports running Java applications using Flink 1.8. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - Added support for Cloud Watch Output and Document Version to the Run Command tasks in Maintenance Windows. + +# __2.10.36__ __2019-12-16__ +## __AWS Comprehend Medical__ + - ### Features + - New Ontology linking APIs will provides medication concepts normalization and Diagnoses codes from input text. In this release we will provide two APIs - RxNorm and ICD10-CM. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - You can now configure your EC2 Fleet to preferentially use EC2 Capacity Reservations for launching On-Demand instances, enabling you to fully utilize the available (and unused) Capacity Reservations before launching On-Demand instances on net new capacity. + +## __Amazon S3__ + - ### Features + - CopyObjectRequest now has `destinationBucket` and `destinationKey` properties for clarity. + The existing names, `bucket` and `key`, are deprecated. + +## __AmazonMQ__ + - ### Features + - Amazon MQ now supports throughput-optimized message brokers, backed by Amazon EBS. + +# __2.10.35__ __2019-12-13__ +## __AWS CodeBuild__ + - ### Features + - CodeBuild adds support for cross account + +## __Amazon Detective__ + - ### Features + - This is the initial release of Amazon Detective. + +## __Amazon Simple Email Service__ + - ### Features + - Added the ability to use your own public-private key pair to configure DKIM authentication for a domain identity. + +# __2.10.34__ __2019-12-12__ +## __AWS SDK for Java v2__ + - ### Bugfixes + - Fixing exception using `RequestBody.fromInputStream` on non-resettable `InputStreams` by making `reset` conditional on `markSupported`. See [#1544](https://github.com/aws/aws-sdk-java-v2/issues/1544) / [#1545](https://github.com/aws/aws-sdk-java-v2/issues/1545) + +## __Access Analyzer__ + - ### Features + - This release includes improvements and fixes bugs for the IAM Access Analyzer feature. + +# __2.10.33__ __2019-12-11__ +## __AWS SDK for Java v2__ + - ### Features + - Adds a `has*` method to requests and responses that have a List or Map property. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release allows customers to attach multiple Elastic Inference Accelerators to a single EC2 instance. It adds support for a Count parameter for each Elastic Inference Accelerator type you specify on the RunInstances and LaunchTemplate APIs. + +# __2.10.32__ __2019-12-10__ +## __AWSKendraFrontendService__ + - ### Features + - 1. Adding DocumentTitleFieldName as an optional configuration for SharePoint. 2. updating s3 object pattern to support all s3 keys. + +# __2.10.31__ __2019-12-09__ +## __AWS Key Management Service__ + - ### Features + - The Verify operation now returns KMSInvalidSignatureException on invalid signatures. The Sign and Verify operations now return KMSInvalidStateException when a request is made against a CMK pending deletion. + +## __Amazon QuickSight__ + - ### Features + - Documentation updates for QuickSight + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - Adds the SSM GetCalendarState API and ChangeCalendar SSM Document type. These features enable the forthcoming Systems Manager Change Calendar feature, which will allow you to schedule events during which actions should (or should not) be performed. + +## __Managed Streaming for Kafka__ + - ### Features + - AWS MSK has added support for Open Monitoring with Prometheus. + +## __Netty NIO HTTP Client__ + - ### Features + - Close HTTP/2 connections if they have had 0 streams for 5 seconds. This can be disabled using `useIdleConnectionReaper(false)` or have the time period adjusted using `connectionMaxIdleTime(...)` on the `NettyNioAsyncHttpClient.Builder`. + - Periodically ping HTTP/2 connections and close them if the service does not respond. The ping periodicity and timeout time is not currently configurable. + +# __2.10.30__ __2019-12-04__ +## __Amazon Kinesis Video Signaling Channels__ + - ### Features + - Announcing support for WebRTC in Kinesis Video Streams, as fully managed capability. You can now use simple APIs to enable your connected devices, web, and mobile apps with real-time two-way media streaming capabilities. + +## __Amazon Kinesis Video Streams__ + - ### Features + - Introduces management of signaling channels for Kinesis Video Streams. + +## __AmazonApiGatewayV2__ + - ### Features + - Amazon API Gateway now supports HTTP APIs (beta), enabling customers to quickly build high performance RESTful APIs that are up to 71% cheaper than REST APIs also available from API Gateway. HTTP APIs are optimized for building APIs that proxy to AWS Lambda functions or HTTP backends, making them ideal for serverless workloads. Using HTTP APIs, you can secure your APIs using OIDC and OAuth 2 out of box, quickly build web applications using a simple CORS experience, and get started immediately with automatic deployment and simple create workflows. + +## __Netty NIO HTTP Client__ + - ### Bugfixes + - Fixed an issue where closing the last stream on a connection that had been closed or received a GOAWAY did not close the connection. + - Fixed an issue where receiving a GOAWAY that would cause the closing of all streams could cause all outstanding streams to be completed successfully instead of exceptionally. + +# __2.10.29__ __2019-12-03__ +## __AWS Lambda__ + - ### Features + - - Added the ProvisionedConcurrency type and operations. Allocate provisioned concurrency to enable your function to scale up without fluctuations in latency. Use PutProvisionedConcurrencyConfig to configure provisioned concurrency on a version of a function, or on an alias. + +## __AWS Step Functions__ + - ### Features + - This release of the AWS Step Functions SDK introduces support for Express Workflows. + +## __Amazon Elastic Block Store__ + - ### Features + - This release introduces the EBS direct APIs for Snapshots: 1. ListSnapshotBlocks, which lists the block indexes and block tokens for blocks in an Amazon EBS snapshot. 2. ListChangedBlocks, which lists the block indexes and block tokens for blocks that are different between two snapshots of the same volume/snapshot lineage. 3. GetSnapshotBlock, which returns the data in a block of an Amazon EBS snapshot. + +## __Amazon Rekognition__ + - ### Features + - This SDK Release introduces APIs for Amazon Rekognition Custom Labels feature (CreateProjects, CreateProjectVersion,DescribeProjects, DescribeProjectVersions, StartProjectVersion, StopProjectVersion and DetectCustomLabels). Also new is AugmentedAI (Human In The Loop) Support for DetectModerationLabels in Amazon Rekognition. + +## __Amazon Relational Database Service__ + - ### Features + - This release adds support for the Amazon RDS Proxy + +## __Amazon S3__ + - ### Bugfixes + - Interacting with an access point in a different region to the one the S3 client is configured for will no longer result in the request being signed for the wrong region and rejected by S3. + +## __Amazon SageMaker Service__ + - ### Features + - You can now use SageMaker Autopilot for automatically training and tuning candidate models using a combination of various feature engineering, ML algorithms, and hyperparameters determined from the user's input data. SageMaker Automatic Model Tuning now supports tuning across multiple algorithms. With Amazon SageMaker Experiments users can create Experiments, ExperimentTrials, and ExperimentTrialComponents to track, organize, and evaluate their ML training jobs. With Amazon SageMaker Debugger, users can easily debug training jobs using a number of pre-built rules provided by Amazon SageMaker, or build custom rules. With Amazon SageMaker Processing, users can run on-demand, distributed, and fully managed jobs for data pre- or post- processing or model evaluation. With Amazon SageMaker Model Monitor, a user can create MonitoringSchedules to automatically monitor endpoints to detect data drift and other issues and get alerted on them. This release also includes the preview version of Amazon SageMaker Studio with Domains, UserProfiles, and Apps. This release also includes the preview version of Amazon Augmented AI to easily implement human review of machine learning predictions by creating FlowDefinitions, HumanTaskUis, and HumanLoops. + +## __Application Auto Scaling__ + - ### Features + - This release supports auto scaling of provisioned concurrency for AWS Lambda. + +# __2.10.28__ __2019-12-03__ +## __AWS Compute Optimizer__ + - ### Features + - Initial release of AWS Compute Optimizer. AWS Compute Optimizer recommends optimal AWS Compute resources to reduce costs and improve performance for your workloads. + +## __AWS Network Manager__ + - ### Features + - This is the initial SDK release for AWS Network Manager. + +## __AWS Outposts__ + - ### Features + - This is the initial release for AWS Outposts, a fully managed service that extends AWS infrastructure, services, APIs, and tools to customer sites. AWS Outposts enables you to launch and run EC2 instances and EBS volumes locally at your on-premises location. This release introduces new APIs for creating and viewing Outposts. + +## __AWS S3 Control__ + - ### Features + - Amazon S3 Access Points is a new S3 feature that simplifies managing data access at scale for shared data sets on Amazon S3. Access Points provide a customizable way to access the objects in a bucket, with a unique hostname and access policy that enforces the specific permissions and network controls for any request made through the access point. This represents a new way of provisioning access to shared data sets. + +## __AWSKendraFrontendService__ + - ### Features + - It is a preview launch of Amazon Kendra. Amazon Kendra is a managed, highly accurate and easy to use enterprise search service that is powered by machine learning. + +## __Amazon Augmented AI Runtime__ + - ### Features + - This release adds support for Amazon Augmented AI, which makes it easy to build workflows for human review of machine learning predictions. + +## __Amazon CodeGuru Profiler__ + - ### Features + - (New Service) Amazon CodeGuru Profiler analyzes application CPU utilization and latency characteristics to show you where you are spending the most cycles in your application. This analysis is presented in an interactive flame graph that helps you easily understand which paths consume the most resources, verify that your application is performing as expected, and uncover areas that can be optimized further. + +## __Amazon CodeGuru Reviewer__ + - ### Features + - This is the preview release of Amazon CodeGuru Reviewer. + +## __Amazon EC2 Container Service__ + - ### Features + - This release supports ECS Capacity Providers, Fargate Spot, and ECS Cluster Auto Scaling. These features enable new ways for ECS to manage compute capacity used by tasks. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds support for the following features: 1. An option to enable acceleration for Site-to-Site VPN connections; 2. Inf1 instances featuring up to 16 AWS Inferentia chips; 3. The ability to associate route tables with internet gateways and virtual private gateways; 4. AWS Local Zones that place compute, storage, database, and other select services; 5. Launching and viewing EC2 instances and EBS volumes running locally in Outposts; 6. Peering Transit Gateways between regions simplifying creation of secure and private global networks on AWS; 7. Transit Gateway Multicast, enabling multicast routing within and between VPCs using Transit Gateway as a multicast router. + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Introducing Amazon EKS with Fargate. Customers can now use Amazon EKS to launch pods directly onto AWS Fargate, the serverless compute engine built for containers on AWS. + +## __Amazon Elasticsearch Service__ + - ### Features + - UltraWarm storage provides a cost-effective way to store large amounts of read-only data on Amazon Elasticsearch Service. Rather than attached storage, UltraWarm nodes use Amazon S3 and a sophisticated caching solution to improve performance. For indices that you are not actively writing to and query less frequently, UltraWarm storage offers significantly lower costs per GiB. In Elasticsearch, these warm indices behave just like any other index. You can query them using the same APIs or use them to create dashboards in Kibana. + +## __Amazon Fraud Detector__ + - ### Features + - Amazon Fraud Detector is a fully managed service that makes it easy to identify potentially fraudulent online activities such as online payment fraud and the creation of fake accounts. Amazon Fraud Detector uses your data, machine learning (ML), and more than 20 years of fraud detection expertise from Amazon to automatically identify potentially fraudulent online activity so you can catch more fraud faster. + +## __Amazon Simple Storage Service__ + - ### Features + - Amazon S3 Access Points is a new S3 feature that simplifies managing data access at scale for shared data sets on Amazon S3. Access Points provide a customizable way to access the objects in a bucket, with a unique hostname and access policy that enforces the specific permissions and network controls for any request made through the access point. This represents a new way of provisioning access to shared data sets. + +## __Amazon Textract__ + - ### Features + - This SDK Release introduces Amazon Augmented AI support for Amazon Textract AnalyzeDocument API. Image byte payloads for synchronous operations have increased from 5 MB to 10 MB. + +# __2.10.27__ __2019-12-02__ +## __Access Analyzer__ + - ### Features + - Introducing AWS IAM Access Analyzer, an IAM feature that makes it easy for AWS customers to ensure that their resource-based policies provide only the intended access to resources outside their AWS accounts. + +# __2.10.26__ __2019-12-02__ +## __AWS License Manager__ + - ### Features + - AWS License Manager now automates discovery of bring-your-own-license usage across the customers organization. With few simple settings, customers can add bring your own license product information along with licensing rules, which would enable License Manager to automatically track the instances that have the specified products installed. If License Manager detects any violation of licensing rules, it would notify the customers designated license administrator to take corrective action. + +## __Amazon DynamoDB Enhanced Client [Preview]__ + - ### Features + - Write operations (put, get, delete) now support 'conditionExpression' + +## __Amazon Elastic Compute Cloud__ + - ### Features + - AWS now provides a new BYOL experience for software licenses, such as Windows and SQL Server, that require a dedicated physical server. You can now enjoy the flexibility and cost effectiveness of using your own licenses on Amazon EC2 Dedicated Hosts, but with the simplicity, resiliency, and elasticity of AWS. You can specify your Dedicated Host management preferences, such as host allocation, host capacity utilization, and instance placement in AWS License Manager. Once set up, AWS takes care of these administrative tasks on your behalf, so that you can seamlessly launch virtual machines (instances) on Dedicated Hosts just like you would launch an EC2 instance with AWS provided licenses. + +## __EC2 Image Builder__ + - ### Features + - This is the first release of EC2 Image Builder, a service that provides a managed experience for automating the creation of EC2 AMIs. + +## __Schemas__ + - ### Features + - This release introduces support for Amazon EventBridge schema registry, making it easy to discover and write code for events in EventBridge. + +# __2.10.25__ __2019-11-26__ +## __AWS Directory Service__ + - ### Features + - This release will introduce optional encryption over LDAP network traffic using SSL certificates between customer's self-managed AD and AWS Directory Services instances. The release also provides APIs for Certificate management. + +## __AWS Kinesis__ + - ### Bugfixes + - Reducing default read timeout and write timeout to 10 seconds for Kinesis client. + +## __AWS MediaTailor__ + - ### Features + - AWS Elemental MediaTailor SDK now allows configuration of the Live Pre-Roll feature for HLS and DASH streams. + +## __AWS Organizations__ + - ### Features + - Introduces the DescribeEffectivePolicy action, which returns the contents of the policy that's in effect for the account. + +## __AWS RDS DataService__ + - ### Features + - Type hints to improve handling of some specific parameter types (date/time, decimal etc) for ExecuteStatement and BatchExecuteStatement APIs + +## __AWS Resource Groups Tagging API__ + - ### Features + - You can use tag policies to help standardize on tags across your organization's resources. + +## __AWSServerlessApplicationRepository__ + - ### Features + - AWS Serverless Application Repository now supports verified authors. Verified means that AWS has made a good faith review, as a reasonable and prudent service provider, of the information provided by the requester and has confirmed that the requester's identity is as claimed. + +## __Amazon Cognito Identity Provider__ + - ### Features + - This release adds a new setting for a user pool to configure which recovery methods a user can use to recover their account via the forgot password operation. + +## __Amazon DynamoDB__ + - ### Features + - 1) Amazon Contributor Insights for Amazon DynamoDB is a diagnostic tool for identifying frequently accessed keys and understanding database traffic trends. 2) Support for displaying new fields when a table's encryption state is Inaccessible or the table have been Archived. + +## __Amazon Elastic Inference__ + - ### Features + - Amazon Elastic Inference allows customers to attach Elastic Inference Accelerators to Amazon EC2 and Amazon ECS tasks, thus providing low-cost GPU-powered acceleration and reducing the cost of running deep learning inference. This release allows customers to add or remove tags for their Elastic Inference Accelerators. + +## __Amazon QuickSight__ + - ### Features + - Documentation updates for QuickSight + +## __Amazon WorkSpaces__ + - ### Features + - For the WorkspaceBundle API, added the image identifier and the time of the last update. + +## __Netty NIO HTTP Client__ + - ### Features + - Detect unhealthy http2 connections when read or write times out by sending PING frames + +# __2.10.24__ __2019-11-25__ +## __AWS CodeBuild__ + - ### Features + - CodeBuild adds support for test reporting + +## __AWS Cost Explorer Service__ + - ### Features + - This launch provides customers with access to Cost Category Public Beta APIs. + +## __AWS Elemental MediaConvert__ + - ### Features + - AWS Elemental MediaConvert SDK has added support for 8K outputs and support for QuickTime Animation Codec (RLE) inputs. + +## __AWS Elemental MediaLive__ + - ### Features + - AWS Elemental MediaLive now supports the ability to create a multiple program transport stream (MPTS). + +## __AWS Elemental MediaPackage VOD__ + - ### Features + - Adds a domain name to PackagingGroups, representing the fully qualified domain name for Assets created in the group. + +## __AWS Greengrass__ + - ### Features + - IoT Greengrass supports machine learning resources in 'No container' mode. + +## __AWS IoT__ + - ### Features + - This release adds: 1) APIs for fleet provisioning claim and template, 2) endpoint configuration and custom domains, 3) support for enhanced custom authentication, d) support for 4 additional audit checks: Device and CA certificate key quality checks, IoT role alias over-permissive check and IoT role alias access to unused services check, 5) extended capability of AWS IoT Rules Engine to support IoT SiteWise rule action. The IoT SiteWise rule action lets you send messages from IoT sensors and applications to IoT SiteWise asset properties + +## __AWS IoT Secure Tunneling__ + - ### Features + - This release adds support for IoT Secure Tunneling to remote access devices behind restricted firewalls. + +## __AWS Key Management Service__ + - ### Features + - AWS Key Management Service (KMS) now enables creation and use of asymmetric Customer Master Keys (CMKs) and the generation of asymmetric data key pairs. + +## __AWS Lambda__ + - ### Features + - Added the function state and update status to the output of GetFunctionConfiguration and other actions. Check the state information to ensure that a function is ready before you perform operations on it. Functions take time to become ready when you connect them to a VPC.Added the EventInvokeConfig type and operations to configure error handling options for asynchronous invocation. Use PutFunctionEventInvokeConfig to configure the number of retries and the maximum age of events when you invoke the function asynchronously.Added on-failure and on-success destination settings for asynchronous invocation. Configure destinations to send an invocation record to an SNS topic, an SQS queue, an EventBridge event bus, or a Lambda function.Added error handling options to event source mappings. This enables you to configure the number of retries, configure the maximum age of records, or retry with smaller batches when an error occurs when a function processes a Kinesis or DynamoDB stream.Added the on-failure destination setting to event source mappings. This enables you to send discarded events to an SNS topic or SQS queue when all retries fail or when the maximum record age is exceeded when a function processes a Kinesis or DynamoDB stream.Added the ParallelizationFactor option to event source mappings to increase concurrency per shard when a function processes a Kinesis or DynamoDB stream. + +## __AWS Resource Access Manager__ + - ### Features + - AWS RAM provides new APIs to view the permissions granted to principals in a resource share. This release also creates corresponding resource shares for supported services that use resource policies, as well as an API to promote them to standard shares that can be managed in RAM. + +## __AWS WAFV2__ + - ### Features + - This release introduces new set of APIs ("wafv2") for AWS WAF. Major changes include single set of APIs for creating/updating resources in global and regional scope, and rules are configured directly into web ACL instead of being referenced. The previous APIs ("waf" and "waf-regional") are now referred as AWS WAF Classic. For more information visit: https://docs.aws.amazon.com/waf/latest/APIReference/Welcome.html + +## __Alexa For Business__ + - ### Features + - API update for Alexa for Business: This update enables the use of meeting room configuration that can be applied to a room profile. These settings help improve and measure utilization on Alexa for Business enabled rooms. New features include end meeting reminders, intelligent room release and room utilization analytics report. + +## __Amazon AppConfig__ + - ### Features + - Introducing AWS AppConfig, a new service that enables customers to quickly deploy validated configurations to applications of any size in a controlled and monitored fashion. + +## __Amazon Athena__ + - ### Features + - This release adds additional query lifecycle metrics to the QueryExecutionStatistics object in GetQueryExecution response. + +## __Amazon CloudWatch__ + - ### Features + - This release adds a new feature called "Contributor Insights". "Contributor Insights" supports the following 6 new APIs (PutInsightRule, DeleteInsightRules, EnableInsightRules, DisableInsightRules, DescribeInsightRules and GetInsightRuleReport). + +## __Amazon CloudWatch Application Insights__ + - ### Features + - CloudWatch Application Insights for .NET and SQL Server includes the follwing features: -Tagging Create and manage tags for your applications.-Custom log pattern matching. Define custom log patterns to be detected and monitored.-Resource-level permissions. Specify applications users can access. + +## __Amazon Cognito Identity Provider__ + - ### Features + - Amazon Cognito Userpools now supports Sign in with Apple as an Identity Provider. + +## __Amazon Comprehend__ + - ### Features + - Amazon Comprehend now supports real-time analysis with Custom Classification + +## __Amazon Data Lifecycle Manager__ + - ### Features + - You can now set time based retention policies on Data Lifecycle Manager. With this launch, DLM allows you to set snapshot retention period in the following interval units: days, weeks, months and years. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds two new APIs: 1. ModifyDefaultCreditSpecification, which allows you to set default credit specification at the account level per AWS Region, per burstable performance instance family, so that all new burstable performance instances in the account launch using the new default credit specification. 2. GetDefaultCreditSpecification, which allows you to get current default credit specification per AWS Region, per burstable performance instance family. This release also adds new client exceptions for StartInstances and StopInstances. + +## __Amazon Kinesis Analytics__ + - ### Features + - Kinesis Data Analytics service adds support to configure Java applications to access resources in a VPC. Also releasing support to configure Java applications to set allowNonRestoreState flag through the service APIs. + +## __Amazon Lex Runtime Service__ + - ### Features + - Amazon Lex adds "sessionId" attribute to the PostText and PostContent response. + +## __Amazon Redshift__ + - ### Features + - This release contains changes for 1. Redshift Scheduler 2. Update to the DescribeNodeConfigurationOptions to include a new action type recommend-node-config + +## __Amazon Relational Database Service__ + - ### Features + - Cluster Endpoints can now be tagged by using --tags in the create-db-cluster-endpoint API + +## __Amazon Simple Email Service__ + - ### Features + - This release includes support for automatically suppressing email addresses that result in hard bounce or complaint events at the account level, and for managing addresses on this account-level suppression list. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - AWS Systems Manager Documents now supports more Document Types: ApplicationConfiguration, ApplicationConfigurationSchema and DeploymentStrategy. This release also extends Document Permissions capabilities and introduces a new Force flag for DeleteDocument API. + +## __Application Auto Scaling__ + - ### Features + - This release supports auto scaling of document classifier endpoints for Comprehend; and supports target tracking based on the average capacity utilization metric for AppStream 2.0 fleets. + +## __Elastic Load Balancing__ + - ### Features + - This release of Elastic Load Balancing V2 adds new subnet features for Network Load Balancers and a new routing algorithm for Application Load Balancers. + +# __2.10.23__ __2019-11-22__ +## __AWS Auto Scaling Plans__ + - ### Features + - Update default endpoint for AWS Auto Scaling. + +## __AWS Certificate Manager__ + - ### Features + - This release adds support for Tag-Based IAM for AWS Certificate Manager and adding tags to certificates upon creation. + +## __AWS CodeBuild__ + - ### Features + - Add Canonical ARN to LogsLocation. + +## __AWS Elemental MediaPackage VOD__ + - ### Features + - Includes the submission time of Asset ingestion request in the API response for Create/List/Describe Assets. + +## __AWS SDK for Java v2__ + - ### Bugfixes + - The ProcessCredentialsProvider now supports credential files up to 64 KB by default through an increase of the processOutputLimit from 1024 bytes to 64000 bytes. + +## __AWS Security Token Service__ + - ### Features + - Support tagging for STS sessions and tag based access control for the STS APIs + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds two new APIs (DescribeInstanceTypes and DescribeInstanceTypeOfferings) that give customers access to instance type attributes and regional and zonal offerings. + +## __Amazon Elastic MapReduce__ + - ### Features + - Amazon EMR adds support for concurrent step execution and cancelling running steps. Amazon EMR has added a new Outpost ARN field in the ListCluster and DescribeCluster API responses that is populated for clusters launched in an AWS Outpost subnet. + +## __Amazon Forecast Service__ + - ### Features + - This release adds two key updates to existing APIs. 1. Amazon Forecast can now generate forecasts in any quantile using the optional parameter forecastTypes in the CreateForecast API and 2. You can get additional details (metrics and relevant error messages) on your AutoML runs using the DescribePredictor and GetAccuracyMetrics APIs. + +## __Amazon Rekognition__ + - ### Features + - This release adds enhanced face filtering support to the IndexFaces API operation, and introduces face filtering for CompareFaces and SearchFacesByImage API operations. + +## __Amazon Simple Notification Service__ + - ### Features + - Added documentation for the dead-letter queue feature. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - Add RebootOption and LastNoRebootInstallOperationTime for DescribeInstancePatchStates and DescribeInstancePatchStatesForPatchGroup API + +## __Application Auto Scaling__ + - ### Features + - Update default endpoint for Application Auto Scaling. + +# __2.10.22__ __2019-11-21__ +## __AWS Amplify__ + - ### Features + - This release of AWS Amplify Console introduces support for backend environments. Backend environments are containers for AWS deployments. Each environment is a collection of AWS resources. + +## __AWS AppSync__ + - ### Features + - AppSync: AWS AppSync now supports the ability to add, configure, and maintain caching for your AWS AppSync GraphQL API. + +## __AWS Config__ + - ### Features + - AWS Config launches Custom Configuration Items. A new feature which allows customers to publish resource configuration for third-party resources, custom, or on-premises servers. + +## __AWS Glue__ + - ### Features + - This release adds support for Glue 1.0 compatible ML Transforms. + +## __AWSMarketplace Metering__ + - ### Features + - Documentation updates for the AWS Marketplace Metering Service. + +## __Amazon Connect Participant Service__ + - ### Features + - This release adds 5 new APIs: CreateParticipantConnection, DisconnectParticipant, GetTranscript, SendEvent, and SendMessage. For Amazon Connect chat, you can use them to programmatically perform participant actions on the configured Amazon Connect instance. Learn more here: https://docs.aws.amazon.com/connect-participant/latest/APIReference/Welcome.html + +## __Amazon Connect Service__ + - ### Features + - This release adds a new API: StartChatContact. You can use it to programmatically start a chat on the specified Amazon Connect instance. Learn more here: https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html + +## __Amazon DynamoDB__ + - ### Features + - With this release, you can convert an existing Amazon DynamoDB table to a global table by adding replicas in other AWS Regions. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds support for attaching AWS License Manager Configurations to Amazon Machine Image (AMI) using ImportImage API; and adds support for running different instance sizes on EC2 Dedicated Hosts + +## __Amazon Lex Model Building Service__ + - ### Features + - Amazon Lex now supports Sentiment Analysis + +## __Amazon Lex Runtime Service__ + - ### Features + - Amazon Lex now supports Sentiment Analysis + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - The release contains new API and API changes for AWS Systems Manager Explorer product. + +## __Amazon Transcribe Service__ + - ### Features + - With this release, Amazon Transcribe now supports transcriptions from audio sources in Hebrew (he-IL), Swiss German (de-CH), Japanese (ja-JP), Turkish (tr-TR), Arabic-Gulf (ar-AE), Malay (ms-MY), Telugu (te-IN) + +# __2.10.21__ __2019-11-20__ +## __AWS Application Discovery Service__ + - ### Features + - New exception type for use with Migration Hub home region + +## __AWS CloudTrail__ + - ### Features + - 1. This release adds two new APIs, GetInsightSelectors and PutInsightSelectors, which let you configure CloudTrail Insights event delivery on a trail. An Insights event is a new type of event that is generated when CloudTrail detects unusual activity in your AWS account. In this release, only "ApiCallRateInsight" is a supported Insights event type. 2. This release also adds the new "ExcludeManagementEventSource" option to the existing PutEventSelectors API. This field currently supports only AWS Key Management Services. + +## __AWS CodeCommit__ + - ### Features + - This release adds support for creating pull request approval rules and pull request approval rule templates in AWS CodeCommit. This allows developers to block merges of pull requests, contingent on the approval rules being satisfiied. + +## __AWS DataSync__ + - ### Features + - Update to configure task to run periodically on a schedule + +## __AWS Elemental MediaStore__ + - ### Features + - This release fixes a broken link in the SDK documentation. + +## __AWS Migration Hub__ + - ### Features + - New exception type for use with Migration Hub home region + +## __AWS Migration Hub Config__ + - ### Features + - AWS Migration Hub Config Service allows you to get and set the Migration Hub home region for use with AWS Migration Hub and Application Discovery Service + +## __AWS Storage Gateway__ + - ### Features + - The new DescribeAvailabilityMonitorTest API provides the results of the most recent High Availability monitoring test. The new StartAvailabilityMonitorTest API verifies the storage gateway is configured for High Availability monitoring. The new ActiveDirectoryStatus response element has been added to the DescribeSMBSettings and JoinDomain APIs to indicate the status of the gateway after the most recent JoinDomain operation. The new TimeoutInSeconds parameter of the JoinDomain API allows for the configuration of the timeout in which the JoinDomain operation must complete. + +## __Amazon Chime__ + - ### Features + - Adds APIs to create and manage meeting session resources for the Amazon Chime SDK + +## __Amazon Data Lifecycle Manager__ + - ### Features + - DLM now supports Fast Snapshot Restore. You can enable Fast Restore on snapshots created by DLM, provide the AZs and the number of snapshots to be enabled with this capability. + +## __Amazon EC2 Container Service__ + - ### Features + - Added support for CPU and memory task-level overrides on the RunTask and StartTask APIs. Added location information to Tasks. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release of Amazon Elastic Compute Cloud (Amazon EC2) introduces support for Amazon Elastic Block Store (Amazon EBS) fast snapshot restores. + +## __Amazon FSx__ + - ### Features + - Announcing a Multi-AZ deployment type for Amazon FSx for Windows File Server, providing fully-managed Windows file storage with high availability and redundancy across multiple AWS Availability Zones. + +## __Amazon Kinesis Firehose__ + - ### Features + - With this release, Amazon Kinesis Data Firehose allows server side encryption with customer managed CMKs. Customer managed CMKs ( "Customer Master Keys") are AWS Key Management Service (KMS) keys that are fully managed by the customer. With customer managed CMKs, customers can establish and maintain their key policies, IAM policies, rotating policies and add tags. For more information about AWS KMS and CMKs, please refer to: https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html. Please refer to the following link to create CMKs: https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-create-cmk.html + +## __Amazon QuickSight__ + - ### Features + - Amazon QuickSight now supports programmatic creation and management of data sources, data sets, dashboards and templates with new APIs. Templates hold dashboard metadata, and can be used to create copies connected to the same or different dataset as required. Also included in this release are APIs for SPICE ingestions, fine-grained access control over AWS resources using AWS Identity and Access Management (IAM) policies, as well AWS tagging. APIs are supported for both Standard and Enterprise Edition, with edition-specific support for specific functionality. + +## __Amazon Simple Storage Service__ + - ### Features + - This release introduces support for Amazon S3 Replication Time Control, a new feature of S3 Replication that provides a predictable replication time backed by a Service Level Agreement. S3 Replication Time Control helps customers meet compliance or business requirements for data replication, and provides visibility into the replication process with new Amazon CloudWatch Metrics. + +## __Amazon Transcribe Service__ + - ### Features + - With this release Amazon Transcribe enables alternative transcriptions so that you can see different interpretations of transcribed audio. + +# __2.10.20__ __2019-11-19__ +## __AWS CloudFormation__ + - ### Features + - This release of AWS CloudFormation StackSets enables users to detect drift on a stack set and the stack instances that belong to that stack set. + +## __AWS CodeBuild__ + - ### Features + - Add support for ARM and GPU-enhanced build environments and a new SSD-backed Linux compute type with additional CPU and memory in CodeBuild + +## __AWS Config__ + - ### Features + - AWSConfig launches support for conformance packs. A conformance pack is a new resource type that allows you to package a collection of Config rules and remediation actions into a single entity. You can create and deploy conformance packs into your account or across all accounts in your organization + +## __AWS Identity and Access Management__ + - ### Features + - IAM reports the timestamp when a role's credentials were last used to make an AWS request. This helps you identify unused roles and remove them confidently from your AWS accounts. + +## __AWS IoT__ + - ### Features + - As part of this release, we are extending the capability of AWS IoT Rules Engine to send messages directly to customer's own web services/applications. Customers can now create topic rules with HTTP actions to route messages from IoT Core directly to URL's that they own. Ownership is proved by creating and confirming topic rule destinations. + +## __AWS Lambda__ + - ### Features + - This release provides three new runtimes to support Node.js 12 (initially 12.13.0), Python 3.8 and Java 11. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - This release adds support for RunInstances to specify the metadata options for new instances; adds a new API, ModifyInstanceMetadataOptions, which lets you modify the metadata options for a running or stopped instance; and adds support for CreateCustomerGateway to specify a device name. + +## __Auto Scaling__ + - ### Features + - Amazon EC2 Auto Scaling now supports Instance Weighting and Max Instance Lifetime. Instance Weighting allows specifying the capacity units for each instance type included in the MixedInstancesPolicy and how they would contribute to your application's performance. Max Instance Lifetime allows specifying the maximum length of time that an instance can be in service. If any instances are approaching this limit, Amazon EC2 Auto Scaling gradually replaces them. + +## __Elastic Load Balancing__ + - ### Features + - This release allows forward actions on Application Load Balancers to route requests to multiple target groups, based on the weight you specify for each target group. + +# __2.10.19__ __2019-11-18__ +## __AWS CloudFormation__ + - ### Features + - This release introduces APIs for the CloudFormation Registry, a new service to submit and discover resource providers with which you can manage third-party resources natively in CloudFormation. + +## __AWS Cost Explorer Service__ + - ### Features + - add EstimatedOnDemandCostWithCurrentCommitment to GetSavingsPlansPurchaseRecommendationRequest API + +## __Amazon Pinpoint__ + - ### Features + - This release of the Amazon Pinpoint API introduces support for using and managing message templates for messages that are sent through the voice channel. It also introduces support for specifying default values for message variables in message templates. + +## __Amazon Relational Database Service__ + - ### Features + - Documentation updates for rds + +## __Amazon SageMaker Runtime__ + - ### Features + - Amazon SageMaker Runtime now supports a new TargetModel header to invoke a specific model hosted on multi model endpoints. + +## __Amazon SageMaker Service__ + - ### Features + - Amazon SageMaker now supports multi-model endpoints to host multiple models on an endpoint using a single inference container. + +## __Amazon Simple Storage Service__ + - ### Features + - Added support for S3 Replication for existing objects. This release allows customers who have requested and been granted access to replicate existing S3 objects across buckets. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - The release contains new API and API changes for AWS Systems Manager Explorer product. + +## __Netty NIO HTTP Client__ + - ### Bugfixes + - Update default connectionMaxIdleTimeout of NettyNioAsyncClient to 5 seconds + +# __2.10.18__ __2019-11-15__ +## __AWS Elemental MediaConvert__ + - ### Features + - AWS Elemental MediaConvert SDK has added support for DolbyVision encoding, and SCTE35 & ESAM insertion to DASH ISO EMSG. + +## __AWS SDK for Java v2__ + - ### Features + - When SdkException or one of its children is created without a 'message', inherit the message from the exception 'cause' (if any). This should reduce the chance of an exception being raised by the SDK with a null message. + +## __Amazon Chime__ + - ### Features + - This release adds support for Chime Room Management APIs + +## __Amazon CloudWatch Logs__ + - ### Features + - Documentation updates for logs + +## __Amazon Cognito Identity Provider__ + - ### Features + - This release adds a new option in the User Pool to allow specifying sender's name in the emails sent by Amazon Cognito. This release also adds support to add SES Configuration Set to the emails sent by Amazon Cognito. + +## __Amazon Elastic Compute Cloud__ + - ### Features + - You can now add tags while copying snapshots. Previously, a user had to first copy the snapshot and then add tags to the copied snapshot manually. Moving forward, you can specify the list of tags you wish to be applied to the copied snapshot as a parameter on the Copy Snapshot API. + +## __Amazon Elastic Kubernetes Service__ + - ### Features + - Introducing Amazon EKS managed node groups, a new feature that lets you easily provision worker nodes for Amazon EKS clusters and keep them up to date using the Amazon EKS management console, CLI, and APIs. + +## __Amazon Elastic MapReduce__ + - ### Features + - Access to the cluster ARN makes it easier for you to author resource-level permissions policies in AWS Identity and Access Management. To simplify the process of obtaining the cluster ARN, Amazon EMR has added a new field containing the cluster ARN to all API responses that include the cluster ID. + +## __Amazon GuardDuty__ + - ### Features + - This release includes new operations related to findings export, including: CreatePublishingDestination, UpdatePublishingDestination, DescribePublishingDestination, DeletePublishingDestination and ListPublishingDestinations. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - This release updates AWS Systems Manager Parameter Store documentation for the enhanced search capability. + +## __Amazon WorkSpaces__ + - ### Features + - Added APIs to register your directories with Amazon WorkSpaces and to modify directory details. + +## __Elastic Load Balancing__ + - ### Features + - Documentation-only change to the default value of the routing.http.drop_invalid_header_fields.enabled attribute. + +# __2.10.17__ __2019-11-14__ +## __AWSMarketplace Metering__ + - ### Features + - Added CustomerNotEntitledException in MeterUsage API for Container use case. + +## __Amazon Cognito Identity Provider__ + - ### Features + - This release adds a new setting at user pool client to prevent user existence related errors during authentication, confirmation, and password recovery related operations. This release also adds support to enable or disable specific authentication flows for a user pool client. + +## __Amazon Connect Service__ + - ### Features + - This release enhances the existing user management APIs and adds 3 new APIs - TagResource, UntagResource, and ListTagsForResource to support tagging Amazon Connect users, which facilitates more granular access controls for Amazon Connect users within an Amazon Connect instance. You can learn more about the new APIs here: https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html. + +## __Amazon Personalize__ + - ### Features + - Amazon Personalize: Adds ability to get batch recommendations by creating a batch inference job. + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - Updates support for adding attachments to Systems Manager Automation documents + +# __2.10.16__ __2019-11-13__ +## __AWS Data Exchange__ + - ### Features + - Introducing AWS Data Exchange, a service that makes it easy for AWS customers to securely create, manage, access, and exchange data sets in the cloud. + +## __AWS IoT__ + - ### Features + - This release adds the custom fields definition support in the index definition for AWS IoT Fleet Indexing Service. Custom fields can be used as an aggregation field to run aggregations with both existing GetStatistics API and newly added GetCardinality, GetPercentiles APIs. GetStatistics will return all statistics (min/max/sum/avg/count...) with this release. For more information, please refer to our latest documentation: https://docs.aws.amazon.com/iot/latest/developerguide/iot-indexing.html + +## __Amazon CloudSearch__ + - ### Features + - Amazon CloudSearch domains let you require that all traffic to the domain arrive over HTTPS. This security feature helps you block clients that send unencrypted requests to the domain. + +## __Amazon Data Lifecycle Manager__ + - ### Features + - You can now add tags to a lifecycle policy in Data Lifecycle Manager (DLM). Tags allow you to categorize your policies in different ways, such as by department, purpose or owner. You can also enable resource level permissions based on tags to set access control on ability to modify or delete a tagged policy. + +## __Amazon Simple Email Service__ + - ### Features + - This is the first release of version 2 of the Amazon SES API. You can use this API to configure your Amazon SES account, and to send email. This API extends the functionality that exists in the previous version of the Amazon SES API. + +# __2.10.15__ __2019-11-12__ +## __AWS CodePipeline__ + - ### Features + - AWS CodePipeline now supports the use of variables in action configuration. + +## __AWS Marketplace Catalog Service__ + - ### Features + - This is the first release for the AWS Marketplace Catalog service which allows you to list, describe and manage change requests on your published entities on AWS Marketplace. + +## __Amazon DynamoDB__ + - ### Features + - Amazon DynamoDB enables you to restore your data to a new DynamoDB table using a point-in-time or on-demand backup. You now can modify the settings on the new restored table. Specifically, you can exclude some or all of the local and global secondary indexes from being created with the restored table. In addition, you can change the billing mode and provisioned capacity settings. + +## __Amazon Transcribe Service__ + - ### Features + - With this release, Amazon Transcribe now supports transcriptions from audio sources in Welsh English (en-WL), Scottish English(en-AB), Irish English(en-IE), Farsi(fa-IR), Tamil(ta-IN), Indonesian(id-ID), Portuguese (pt-PT), Dutch(nl-NL). + +## __Elastic Load Balancing__ + - ### Features + - You can configure your Application Load Balancer to either drop invalid header fields or forward them to targets. + +# __2.10.14__ __2019-11-11__ +## __AWS CloudFormation__ + - ### Features + - The Resource Import feature enables customers to import existing AWS resources into new or existing CloudFormation Stacks. + +## __AWS Cost Explorer Service__ + - ### Features + - This launch provides customers with access to GetCostAndUsageWithResources API. + +## __Amazon Polly__ + - ### Features + - Add `PollyPresigner` which enables support for presigning `SynthesizeSpeech` requests. + +# __2.10.13__ __2019-11-08__ +## __Amazon Cognito Identity__ + - ### Features + - This release adds support for disabling classic flow. + +## __Amazon EC2 Container Registry__ + - ### Features + - This release contains ticket fixes for Amazon ECR. + +# __2.10.12__ __2019-11-07__ +## __AWS S3__ + - ### Features + - Added support for presignPutObject in S3Presigner. + +## __AWS SSO OIDC__ + - ### Features + - This is an initial release of AWS Single Sign-On OAuth device code authorization service. + +## __AWS Single Sign-On__ + - ### Features + - This is an initial release of AWS Single Sign-On (SSO) end-user access. This release adds support for accessing AWS accounts assigned in AWS SSO using short term credentials. + +## __Amazon Comprehend__ + - ### Features + - This release adds new languages (ar, hi, ko, ja, zh, zh-TW) for Amazon Comprehend's DetectSentiment, DetectEntities, DetectKeyPhrases, BatchDetectSentiment, BatchDetectEntities and BatchDetectKeyPhrases APIs + +## __Amazon Simple Systems Manager (SSM)__ + - ### Features + - AWS Systems Manager Session Manager target length increased to 400. + +## __Netty NIO HTTP Client__ + - ### Features + - Switch from setting the absolute URI in HTTP requests with no `Host` header to setting the absolute request path and query paramters and a `Host` header. + +# __2.10.11__ __2019-11-06__ +## __AWS SDK for Java v2__ + - ### Features + - Added the web identity credentials provider to the default credential chain + +## __AWS Savings Plans__ + - ### Features + - This is the first release of Savings Plans, a new flexible pricing model that offers low prices on Amazon EC2 and AWS Fargate usage. + +# __2.10.10__ __2019-11-06__ +## __AWS Budgets__ + - ### Features + - Documentation updates for budgets to track Savings Plans utilization and coverage + +## __AWS CodeBuild__ + - ### Features + - Add support for Build Number, Secrets Manager and Exported Environment Variables. + +## __AWS Cost Explorer Service__ + - ### Features + - This launch provides customers with access to Savings Plans management APIs. + +## __AWS Savings Plans__ + - ### Features + - This is the first release of Savings Plans, a new flexible pricing model that offers low prices on Amazon EC2 and AWS Fargate usage. + +## __AWS Signer__ + - ### Features + - This release adds support for tagging code-signing profiles in AWS Signer. + +## __Amazon Elastic File System__ + - ### Features + - EFS customers can select a lifecycle policy that automatically moves files that have not been accessed for 7 days into the EFS Infrequent Access (EFS IA) storage class. EFS IA provides price/performance that is cost-optimized for files that are not accessed every day. + +# __2.10.9__ __2019-11-05__ +## __AWS CodeStar Notifications__ + - ### Features + - This release adds a notification manager for events in repositories, build projects, deployments, and pipelines. You can now configure rules and receive notifications about events that occur for resources. Each notification includes a status message as well as a link to the resource (repository, build project, deployment application, or pipeline) whose event generated the notification. + +## __Amazon Relational Database Service__ + - ### Features + - Documentation updates for Amazon RDS + +# __2.10.8__ __2019-11-04__ +## __AWS RoboMaker__ + - ### Features + - RoboMaker Fleet Management launch a feature to verify your robot is ready to download and install the new robot application using a download condition file, which is a script run on the robot prior to downloading the new deployment. + +## __Amazon DynamoDB Accelerator (DAX)__ + - ### Features + - Documentation updates for dax + +## __Amazon Elastic Compute Cloud__ + - ### Features + - Documentation updates for ec2 + +# __2.10.7__ __2019-11-01__ +## __AWS CloudTrail__ + - ### Features + - This release adds two new APIs, GetTrail and ListTrails, and support for adding tags when you create a trail by using a new TagsList parameter on CreateTrail operations. + +## __AWS Database Migration Service__ + - ### Features + - This release contains task timeline attributes in replication task statistics. This release also adds a note to the documentation for the CdcStartPosition task request parameter. This note describes how to enable the use of native CDC start points for a PostgreSQL source by setting the new slotName extra connection attribute on the source endpoint to the name of an existing logical replication slot. + +## __Amazon Pinpoint__ + - ### Features + - This release of the Amazon Pinpoint API introduces support for using and managing journeys, and querying analytics data for journeys. + # __2.10.6__ __2019-10-31__ ## __AWS Amplify__ - ### Features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f040805e1bd..fe86c5fb7638 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -88,7 +88,7 @@ Please keep the following in mind when considering a code contribution: Any code you submit will be released under this license. If you are contributing a large/substantial feature, you may be asked to sign a - [Contributor License Agreement (CLA)][cla]. + Contributor License Agreement (CLA). * For anything but very small or quick changes, you should always start by checking the [Issues][issues] page to see if the work is already being done by another person. @@ -165,7 +165,6 @@ when contributing to the SDK. [markdown]: https://guides.github.com/features/mastering-markdown/ [issues]: https://github.com/aws/aws-sdk-java-v2/issues [pull-requests]: https://github.com/aws/aws-sdk-java-v2/pulls -[cla]: https://github.com/aws/aws-cla [label-bug]: https://github.com/aws/aws-sdk-java-v2/labels/Bug [label-doc-issue]: https://github.com/aws/aws-sdk-java-v2/labels/Documentation%20Issue [label-feature-request]: https://github.com/aws/aws-sdk-java-v2/labels/Feature%20Request diff --git a/NOTICE.txt b/NOTICE.txt index 434630a6dbfc..e277e4c2c445 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ AWS SDK for Java 2.0 -Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. This product includes software developed by Amazon Technologies, Inc (http://www.amazon.com/). @@ -10,5 +10,7 @@ THIRD PARTY COMPONENTS This software includes third party software subject to the following copyrights: - XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty. - PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc. +- Apache Commons Lang - https://github.com/apache/commons-lang +- Netty Reactive Streams - https://github.com/playframework/netty-reactive-streams The licenses for these third party components are included in LICENSE.txt diff --git a/README.md b/README.md index 1ec46c292273..9eff33ae8e04 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ artifact source. * [Sample Code](#sample-code) * [API Docs][docs-api] * [Developer Guide][docs-guide] ([source][docs-guide-source]) +* [Maven Archetypes](archetypes/README.md) * [Issues][sdk-issues] * [SDK Blog][blog] * [Giving Feedback](#giving-feedback) @@ -48,7 +49,7 @@ To automatically manage module versions (currently all modules have the same ver software.amazon.awssdk bom - 2.10.6 + 2.11.7 pom import @@ -82,12 +83,12 @@ Alternatively you can add dependencies for the specific services you use only: software.amazon.awssdk ec2 - 2.10.6 + 2.11.7 software.amazon.awssdk s3 - 2.10.6 + 2.11.7 ``` @@ -99,7 +100,7 @@ You can import the whole SDK into your project (includes *ALL* services). Please software.amazon.awssdk aws-sdk-java - 2.10.6 + 2.11.7 ``` diff --git a/archetypes/README.md b/archetypes/README.md new file mode 100755 index 000000000000..70980eb12890 --- /dev/null +++ b/archetypes/README.md @@ -0,0 +1,9 @@ +# Maven Archetypes for AWS SDK for Java 2.x + +## Description +This module contains maven archetypes for AWS Java SDK 2.x. + +## Archetypes + +- [archetype-lambda](archetype-lambda/README.md) - a lambda function template using AWS Java SDK 2.x + diff --git a/archetypes/archetype-lambda/README.md b/archetypes/archetype-lambda/README.md new file mode 100755 index 000000000000..1992b020ddfa --- /dev/null +++ b/archetypes/archetype-lambda/README.md @@ -0,0 +1,65 @@ +# Maven Archetype for lambda function using AWS SDK for Java 2.x + +## Description +This is an Apache Maven Archetype to create a lambda function template using [AWS Java SDK 2.x][aws-java-sdk-v2]. The generated template +has the optimized configurations and follows the best practices to reduce start up time. + +## Usage + +You can use `mvn archetype:generate` to generate a project using this archetype. See [maven archetype usage guidance][maven-archetype-usage] for more information. + +- Interactive mode + +``` +mvn archetype:generate \ + -DarchetypeGroupId=software.amazon.awssdk \ + -DarchetypeArtifactId=archetype-lambda \ + -DarchetypeVersion=2.x\ +``` + +- Batch mode + +``` +mvn archetype:generate \ + -DarchetypeGroupId=software.amazon.awssdk \ + -DarchetypeArtifactId=archetype-lambda \ + -DarchetypeVersion=2.x\ + -DgroupId=com.test \ + -DartifactId=sample-project \ + -Dservice=s3 \ + -Dregion=us-west-2 \ + -DinteractiveMode=false \ +``` + +### Parameters + +Parameter Name | Default Value | Description +---|---|--- +`service` (required) | n/a | Specifies the service client to be used in the lambda function, eg: s3, dynamodb. You can find available services [here][java-sdk-v2-services]. +`region` (required) | n/a | Specifies the region to be set for the SDK client in the application +`groupId`(required) | n/a | Specifies the group ID of the project +`artifactId`(required) | n/a | Specifies the artifact ID of the project +`httpClient` | url-connection-client | Specifies the http client to be used by the SDK client. Available options are `url-connection-client` (sync), `apache-client` (sync), `netty-nio-client` (async). See [http clients][sdk-http-clients] +`handlerClassName` | `"App"`| Specifies the class name of the handler, which will be used as the lambda function name. It should be camel case. +`javaSdkVersion` | Same version as the archetype version | Specifies the version of the AWS Java SDK 2.x to be used +`version` | 1.0-SNAPSHOT | Specifies the version of the project +`package` | ${groupId} | Specifies the package name for the classes + +### Deployment + +To deploy the lambda function, you can use [SAM CLI][sam-cli]. The generated project contains a default [SAM template][sam-template] file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. + +``` +sam deploy --guided +``` + +Please refer to [deploying lambda apps][deploying-lambda-apps] for more info. + +[aws-java-sdk-v2]: https://github.com/aws/aws-sdk-java-v2 +[java-sdk-v2-services]: https://github.com/aws/aws-sdk-java-v2/tree/master/services +[sdk-http-clients]: https://github.com/aws/aws-sdk-java-v2/tree/master/http-clients +[deploying-lambda-apps]: https://docs.aws.amazon.com/lambda/latest/dg/deploying-lambda-apps.html +[sam-cli]:https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html +[maven-archetype-usage]: https://maven.apache.org/archetype/maven-archetype-plugin/usage.html +[sam-template]: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html diff --git a/archetypes/archetype-lambda/pom.xml b/archetypes/archetype-lambda/pom.xml new file mode 100644 index 000000000000..c8dc4e8715e6 --- /dev/null +++ b/archetypes/archetype-lambda/pom.xml @@ -0,0 +1,123 @@ + + + + + + archetypes + software.amazon.awssdk + 2.11.8-SNAPSHOT + + 4.0.0 + archetype-lambda + maven-archetype + AWS Java SDK :: Archetype Lambda + + The AWS SDK for Java - Maven archetype for Java lambda function using AWS Java SDK 2.x + + + + 3.1.2 + 1.6.0 + + + + + + software.amazon.awssdk + aws-sdk-java + ${awsjavasdk.version} + test + + + + + + + src/main/resources + true + + META-INF/maven/archetype-metadata.xml + + + + src/main/resources + false + + META-INF/maven/archetype-metadata.xml + + + + + + org.apache.maven.archetype + archetype-packaging + ${maven.archetype.version} + + + + + + exec-maven-plugin + org.codehaus.mojo + ${exec-maven-plugin.version} + + + map-service-to-client-prefix + generate-resources + + exec + + + python + ${basedir}/src/main/resources/map-service-to-client-prefix + + + + + + maven-archetype-plugin + ${maven.archetype.version} + + true + true + ${skip.unit.tests} + + + + integration-test + verify + + integration-test + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + + true + + + + + \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetypes/archetype-lambda/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 000000000000..cae4983caaf1 --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,49 @@ + + + + + src/main/java + + **/*.java + + + + src/test/java + + **/*.java + + + + + + .gitignore + template.yaml + README.md + + + + + + App + + + ${project.version} + \d+\.\d+.\d+ + + + + + url-connection-client + (url-connection-client|apache-client|netty-nio-client) + + + ^\w+-(\w+-)+\d+$ + + + + ${netty-open-ssl-version} + + + diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/.gitignore b/archetypes/archetype-lambda/src/main/resources/archetype-resources/.gitignore new file mode 100644 index 000000000000..a500caff54ac --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/.gitignore @@ -0,0 +1,17 @@ +# Eclipse +.classpath +.project +.settings/ + +# Intellij +.idea/ +*.iml +*.iws + +# Mac +.DS_Store + +# Maven +target/ + +**/dependency-reduced-pom.xml diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/README.md b/archetypes/archetype-lambda/src/main/resources/archetype-resources/README.md new file mode 100644 index 000000000000..f8effbaa17ad --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/README.md @@ -0,0 +1,45 @@ +#[[#]]# ${handlerClassName} + +This project contains an AWS Lambda maven application with [AWS Java SDK 2.x](https://github.com/aws/aws-sdk-java-v2) dependencies. + +#[[##]]# Prerequisites +- Java 1.8+ +- Apache Maven +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Docker + +#[[##]]# Development + +The generated function handler class just returns the input. The configured AWS Java SDK client is created in `DependencyFactory` class and you can +add the code to interact with the SDK client based on your use case. + +#[[####]]# Building the project +``` +mvn clean install +``` + +#[[####]]# Testing it locally +``` +sam local invoke +``` + +#[[####]]# Adding more SDK clients +To add more service clients, you need to add the specific services modules in `pom.xml` and create the clients in `DependencyFactory` following the same +pattern as ${serviceClientVariable}Client. + +#[[##]]# Deployment + +The generated project contains a default [SAM template](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. You might also need to add specific policies to the lambda function +so that it can access other AWS resources. + +To deploy the application, you can run the following command: + +``` +sam deploy --guided +``` + +See [Deploying Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-deploying.html) for more info. + + + diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/pom.xml b/archetypes/archetype-lambda/src/main/resources/archetype-resources/pom.xml new file mode 100644 index 000000000000..035954af7d98 --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,108 @@ +#parse ( "global.vm") + + + 4.0.0 + + ${groupId} + ${artifactId} + ${version} + jar + + UTF-8 + 1.8 + 1.8 + 3.1.1 + 3.6.1 + 1.6.0 + ${javaSdkVersion} + 1.2.0 + 5.4.2 +#if( $httpClient == 'netty-nio-client') + ${nettyOpenSslVersion} +#end + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + software.amazon.awssdk + ${moduleName} + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk + ${httpClient} + + +#if( $httpClient == 'netty-nio-client') + + + io.netty + netty-tcnative-boringssl-static + ${netty.openssl.version} + +#end + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + false + ${artifactId} + + + + package + + shade + + + + + + + diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/main/java/DependencyFactory.java b/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/main/java/DependencyFactory.java new file mode 100644 index 000000000000..f9f4ed2f2e3b --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/main/java/DependencyFactory.java @@ -0,0 +1,27 @@ +#parse ( "global.vm") + +package ${package}; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.http.${httpClientPackageName}; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.${servicePackage}.${serviceClientClassName}; + +/** + * The module containing all dependencies required by the {@link ${handlerClassName}}. + */ +public class DependencyFactory { + + private DependencyFactory() {} + + /** + * @return an instance of ${serviceClientClassName} + */ + public static ${serviceClientClassName} ${serviceClientVariable}Client() { + return ${serviceClientClassName}.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.${regionEnum}) + .httpClientBuilder(${httpClientClassName}.builder()) + .build(); + } +} diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/main/java/__handlerClassName__.java b/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/main/java/__handlerClassName__.java new file mode 100644 index 000000000000..8a66c4aa925d --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/main/java/__handlerClassName__.java @@ -0,0 +1,29 @@ +#parse ( "global.vm") +package ${package}; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.${servicePackage}.${serviceClientClassName}; + +/** + * Lambda function entry point. You can change to use other pojo type or implement + * a different RequestHandler. + * + * @see Lambda Java Handler for more information + */ +public class ${handlerClassName} implements RequestHandler { + private final ${serviceClientClassName} ${serviceClientVariable}Client; + + public ${handlerClassName}() { + // Initialize the SDK client outside of the handler method so that it can be reused for subsequent invocations. + // It is initialized when the class is loaded. + ${serviceClientVariable}Client = DependencyFactory.${serviceClientVariable}Client(); + // Consider invoking a simple api here to pre-warm up the application, eg: dynamodb#listTables + } + + @Override + public Object handleRequest(final Object input, final Context context) { + // TODO: invoking the api call using ${serviceClientVariable}Client. + return input; + } +} diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/test/java/__handlerClassName__Test.java b/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/test/java/__handlerClassName__Test.java new file mode 100644 index 000000000000..beecf7a3c67a --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/src/test/java/__handlerClassName__Test.java @@ -0,0 +1,18 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +package ${package}; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ${handlerClassName}Test { + + @Test + public void handleRequest_shouldReturnConstantValue() { + ${handlerClassName} function = new ${handlerClassName}(); + Object result = function.handleRequest("echo", null); + assertEquals("echo", result); + } +} diff --git a/archetypes/archetype-lambda/src/main/resources/archetype-resources/template.yaml b/archetypes/archetype-lambda/src/main/resources/archetype-resources/template.yaml new file mode 100644 index 000000000000..5d6bb1592002 --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/archetype-resources/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + # See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + # for more info to see how to tune the lambda function configs based on your use case. + ${handlerClassName}Function: + Type: AWS::Serverless::Function + Properties: + Runtime: java8 + Handler: ${package}.${handlerClassName}::handleRequest + Timeout: 60 + MemorySize: 512 + CodeUri: ./target/${artifactId}.jar + # Attach policies here to give the function permission to access other AWS resources if needed + # See: https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst + # eg: + #Policies: + # - S3ReadPolicy: + # BucketName: test-bucket \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/main/resources/global.vm b/archetypes/archetype-lambda/src/main/resources/global.vm new file mode 100644 index 000000000000..610b556958ff --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/global.vm @@ -0,0 +1,36 @@ +## global variables used by the project +#parse ("serviceMapping.vm") +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +## customize wafregional and dynamodbstreams because they reside in waf and dynamodb modules respectively. +#if ($service == 'dynamodbstreams') + #set ($moduleName = 'dynamodb') + #set ($serviceClientPrefix = 'DynamoDbStreams') + #set ($servicePackage = 'dynamodb.streams') +#elseif ($service == 'wafregional') + #set ($moduleName = 'waf') + #set ($serviceClientPrefix = 'WafRegional') + #set ($servicePackage = 'waf.regional') +#else +## map the serviceId to service package and service client class name + #set ( $servicePackage = $service) + #set ($moduleName = $service) + #set ( $serviceClientPrefix = $serviceMapping[$service]) +#end +#set ( $serviceClientVariable = $serviceClientPrefix.substring(0,1).toLowerCase() + $serviceClientPrefix.substring(1)) +#set( $regionEnum = $region.replace("-", "_").toUpperCase() ) +## map the client module name to the client class name and pacakge name +#if( $httpClient == 'url-connection-client') + #set ($httpClientClassName = 'UrlConnectionHttpClient') + #set ($httpClientPackageName = 'urlconnection.' + $httpClientClassName) + #set ($serviceClientClassName = $serviceClientPrefix + 'Client') +#elseif ( $httpClient == 'apache-client') + #set ($httpClientClassName = 'ApacheHttpClient') + #set ($httpClientPackageName = 'apache.' + $httpClientClassName) + #set ($serviceClientClassName = $serviceClientPrefix + 'Client') +#elseif ( $httpClient == 'netty-nio-client') + #set ($httpClientClassName = 'NettyNioAsyncHttpClient') + #set ($httpClientPackageName = 'nio.netty.' + $httpClientClassName) + #set ($serviceClientClassName = $serviceClientPrefix + 'AsyncClient') +#end diff --git a/archetypes/archetype-lambda/src/main/resources/map-service-to-client-prefix b/archetypes/archetype-lambda/src/main/resources/map-service-to-client-prefix new file mode 100755 index 000000000000..8574844e7283 --- /dev/null +++ b/archetypes/archetype-lambda/src/main/resources/map-service-to-client-prefix @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +import json +import os +import string + +MAPPING_FILE_NAME = 'serviceMapping.vm' +RESOURCES_ROOT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__))) +ARCHETYPE_LAMBDA_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(RESOURCES_ROOT_DIR, "../")))) +SERVICE_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(os.path.join(__file__, "../../../../")))), + 'services' +) + +def load_all_service_modules(): + service_mapping = {} + for f in [f for f in os.listdir(SERVICE_DIR) if os.path.isdir(os.path.join(SERVICE_DIR, f)) & os.path.exists(os.path.join(SERVICE_DIR, f, 'target'))]: + for s in [s for s in os.listdir(os.path.join(SERVICE_DIR, f, 'target', 'generated-sources/sdk/software/amazon/awssdk/services', f)) if s.endswith('AsyncClient.java') & s.startswith('Default')]: + service_mapping[f] = find_client_prefix(s) + return service_mapping + +def find_client_prefix(d): + index = d.find('AsyncClient.java') + return d[7:index] + +def write_to_vm_file(service_mapping): + target = os.path.join(ARCHETYPE_LAMBDA_ROOT_DIR, 'target') + + if not os.path.exists(target): + os.mkdir(target) + + target = os.path.join(ARCHETYPE_LAMBDA_ROOT_DIR, 'target', 'classes') + + if not os.path.exists(target): + os.mkdir(target) + + filename = os.path.join(target, MAPPING_FILE_NAME) + + with open(filename, 'w') as f: + f.write('#set ( $serviceMapping =') + f.write(json.dumps(service_mapping)) + f.write(')') + return filename + +def main(): + service_mapping = load_all_service_modules() + write_to_vm_file(service_mapping) + +if __name__ == '__main__': + main() diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/archetype.properties b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/archetype.properties new file mode 100644 index 000000000000..682c1bdf1f84 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/archetype.properties @@ -0,0 +1,10 @@ +groupId=software.amazonaws.test +artifactId=test-apache-artifact +version=1.0-SNAPSHOT +package=software.amazonaws.test +service=dynamodb +httpClient=apache-client +handlerClassName=MyApacheFunction +region=ap-southeast-1 +javaSdkVersion=2.11.0 +nettyOpenSslVersion=2.0.29.Final \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/goal.txt b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/goal.txt new file mode 100644 index 000000000000..4a1a71d3364c --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/goal.txt @@ -0,0 +1 @@ +verify \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/README.md b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/README.md new file mode 100644 index 000000000000..30bf8a316d7e --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/README.md @@ -0,0 +1,45 @@ +# MyApacheFunction + +This project contains an AWS Lambda maven application with [AWS Java SDK 2.x](https://github.com/aws/aws-sdk-java-v2) dependencies. + +## Prerequisites +- Java 1.8+ +- Apache Maven +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Docker + +## Development + +The generated function handler class just returns the input. The configured AWS Java SDK client is created in `DependencyFactory` class and you can +add the code to interact with the SDK client based on your use case. + +#### Building the project +``` +mvn clean install +``` + +#### Testing it locally +``` +sam local invoke +``` + +#### Adding more SDK clients +To add more service clients, you need to add the specific services modules in `pom.xml` and create the clients in `DependencyFactory` following the same +pattern as dynamoDbClient. + +## Deployment + +The generated project contains a default [SAM template](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. You might also need to add specific policies to the lambda function +so that it can access other AWS resources. + +To deploy the application, you can run the following command: + +``` +sam deploy --guided +``` + +See [Deploying Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-deploying.html) for more info. + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/pom.xml b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/pom.xml new file mode 100644 index 000000000000..2f1c10b9e1e4 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + software.amazonaws.test + test-apache-artifact + 1.0-SNAPSHOT + jar + + UTF-8 + 1.8 + 1.8 + 3.1.1 + 3.6.1 + 1.6.0 + 2.11.0 + 1.2.0 + 5.4.2 + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + software.amazon.awssdk + dynamodb + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk + apache-client + + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + false + test-apache-artifact + + + + package + + shade + + + + + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java new file mode 100644 index 000000000000..f6ecbd48a31f --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java @@ -0,0 +1,26 @@ + +package software.amazonaws.test; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +/** + * The module containing all dependencies required by the {@link MyApacheFunction}. + */ +public class DependencyFactory { + + private DependencyFactory() {} + + /** + * @return an instance of DynamoDbClient + */ + public static DynamoDbClient dynamoDbClient() { + return DynamoDbClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.AP_SOUTHEAST_1) + .httpClientBuilder(ApacheHttpClient.builder()) + .build(); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/main/java/software/amazonaws/test/MyApacheFunction.java b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/main/java/software/amazonaws/test/MyApacheFunction.java new file mode 100644 index 000000000000..8f9f860811c6 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/main/java/software/amazonaws/test/MyApacheFunction.java @@ -0,0 +1,28 @@ +package software.amazonaws.test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +/** + * Lambda function entry point. You can change to use other pojo type or implement + * a different RequestHandler. + * + * @see Lambda Java Handler for more information + */ +public class MyApacheFunction implements RequestHandler { + private final DynamoDbClient dynamoDbClient; + + public MyApacheFunction() { + // Initialize the SDK client outside of the handler method so that it can be reused for subsequent invocations. + // It is initialized when the class is loaded. + dynamoDbClient = DependencyFactory.dynamoDbClient(); + // Consider invoking a simple api here to pre-warm up the application, eg: dynamodb#listTables + } + + @Override + public Object handleRequest(final Object input, final Context context) { + // TODO: invoking the api call using dynamoDbClient. + return input; + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/test/java/software/amazonaws/test/MyApacheFunctionTest.java b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/test/java/software/amazonaws/test/MyApacheFunctionTest.java new file mode 100644 index 000000000000..b55287348392 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/src/test/java/software/amazonaws/test/MyApacheFunctionTest.java @@ -0,0 +1,15 @@ +package software.amazonaws.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MyApacheFunctionTest { + + @Test + public void handleRequest_shouldReturnConstantValue() { + MyApacheFunction function = new MyApacheFunction(); + Object result = function.handleRequest("echo", null); + assertEquals("echo", result); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/template.yaml b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/template.yaml new file mode 100644 index 000000000000..513c0aed185b --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/apachehttpclient/reference/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + # See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + # for more info to see how to tune the lambda function configs based on your use case. + MyApacheFunctionFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: java8 + Handler: software.amazonaws.test.MyApacheFunction::handleRequest + Timeout: 60 + MemorySize: 512 + CodeUri: ./target/test-apache-artifact.jar + # Attach policies here to give the function permission to access other AWS resources if needed + # See: https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst + # eg: + #Policies: + # - S3ReadPolicy: + # BucketName: test-bucket \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/archetype.properties b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/archetype.properties new file mode 100644 index 000000000000..148e110f2ee0 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/archetype.properties @@ -0,0 +1,25 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file 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. +# + +groupId=software.amazonaws.test +artifactId=test-dynamodbstreams-artifact +version=1.0-SNAPSHOT +package=software.amazonaws.test +service=dynamodbstreams +httpClient=apache-client +handlerClassName=MyDynamoDbStreamsFunction +region=ap-southeast-1 +javaSdkVersion=2.11.0 +nettyOpenSslVersion=2.0.29.Final \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/goal.txt b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/goal.txt new file mode 100644 index 000000000000..4a1a71d3364c --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/goal.txt @@ -0,0 +1 @@ +verify \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/README.md b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/README.md new file mode 100644 index 000000000000..2fc00d2e4ac0 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/README.md @@ -0,0 +1,45 @@ +# MyDynamoDbStreamsFunction + +This project contains an AWS Lambda maven application with [AWS Java SDK 2.x](https://github.com/aws/aws-sdk-java-v2) dependencies. + +## Prerequisites +- Java 1.8+ +- Apache Maven +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Docker + +## Development + +The generated function handler class just returns the input. The configured AWS Java SDK client is created in `DependencyFactory` class and you can +add the code to interact with the SDK client based on your use case. + +#### Building the project +``` +mvn clean install +``` + +#### Testing it locally +``` +sam local invoke +``` + +#### Adding more SDK clients +To add more service clients, you need to add the specific services modules in `pom.xml` and create the clients in `DependencyFactory` following the same +pattern as dynamoDbStreamsClient. + +## Deployment + +The generated project contains a default [SAM template](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. You might also need to add specific policies to the lambda function +so that it can access other AWS resources. + +To deploy the application, you can run the following command: + +``` +sam deploy --guided +``` + +See [Deploying Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-deploying.html) for more info. + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/pom.xml b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/pom.xml new file mode 100644 index 000000000000..fa7e7cce3210 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + software.amazonaws.test + test-dynamodbstreams-artifact + 1.0-SNAPSHOT + jar + + UTF-8 + 1.8 + 1.8 + 3.1.1 + 3.6.1 + 1.6.0 + 2.11.0 + 1.2.0 + 5.4.2 + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + software.amazon.awssdk + dynamodb + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk + apache-client + + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + false + test-dynamodbstreams-artifact + + + + package + + shade + + + + + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java new file mode 100644 index 000000000000..b8bdd17acc85 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java @@ -0,0 +1,26 @@ + +package software.amazonaws.test; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.streams.DynamoDbStreamsClient; + +/** + * The module containing all dependencies required by the {@link MyDynamoDbStreamsFunction}. + */ +public class DependencyFactory { + + private DependencyFactory() {} + + /** + * @return an instance of DynamoDbStreamsClient + */ + public static DynamoDbStreamsClient dynamoDbStreamsClient() { + return DynamoDbStreamsClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.AP_SOUTHEAST_1) + .httpClientBuilder(ApacheHttpClient.builder()) + .build(); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/main/java/software/amazonaws/test/MyDynamoDbStreamsFunction.java b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/main/java/software/amazonaws/test/MyDynamoDbStreamsFunction.java new file mode 100644 index 000000000000..2722fbfdba68 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/main/java/software/amazonaws/test/MyDynamoDbStreamsFunction.java @@ -0,0 +1,28 @@ +package software.amazonaws.test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.dynamodb.streams.DynamoDbStreamsClient; + +/** + * Lambda function entry point. You can change to use other pojo type or implement + * a different RequestHandler. + * + * @see Lambda Java Handler for more information + */ +public class MyDynamoDbStreamsFunction implements RequestHandler { + private final DynamoDbStreamsClient dynamoDbStreamsClient; + + public MyDynamoDbStreamsFunction() { + // Initialize the SDK client outside of the handler method so that it can be reused for subsequent invocations. + // It is initialized when the class is loaded. + dynamoDbStreamsClient = DependencyFactory.dynamoDbStreamsClient(); + // Consider invoking a simple api here to pre-warm up the application, eg: dynamodb#listTables + } + + @Override + public Object handleRequest(final Object input, final Context context) { + // TODO: invoking the api call using dynamoDbStreamsClient. + return input; + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/test/java/software/amazonaws/test/MyDynamoDbStreamsFunctionTest.java b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/test/java/software/amazonaws/test/MyDynamoDbStreamsFunctionTest.java new file mode 100644 index 000000000000..7553684cf11a --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/src/test/java/software/amazonaws/test/MyDynamoDbStreamsFunctionTest.java @@ -0,0 +1,15 @@ +package software.amazonaws.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MyDynamoDbStreamsFunctionTest { + + @Test + public void handleRequest_shouldReturnConstantValue() { + MyDynamoDbStreamsFunction function = new MyDynamoDbStreamsFunction(); + Object result = function.handleRequest("echo", null); + assertEquals("echo", result); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/template.yaml b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/template.yaml new file mode 100644 index 000000000000..797f24dc0751 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/dynamodbstreamsclient/reference/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + # See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + # for more info to see how to tune the lambda function configs based on your use case. + MyDynamoDbStreamsFunctionFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: java8 + Handler: software.amazonaws.test.MyDynamoDbStreamsFunction::handleRequest + Timeout: 60 + MemorySize: 512 + CodeUri: ./target/test-dynamodbstreams-artifact.jar + # Attach policies here to give the function permission to access other AWS resources if needed + # See: https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst + # eg: + #Policies: + # - S3ReadPolicy: + # BucketName: test-bucket \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/archetype.properties b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/archetype.properties new file mode 100644 index 000000000000..b5ef44cbd770 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/archetype.properties @@ -0,0 +1,11 @@ +groupId=software.amazonaws.test +artifactId=test-netty-artifact +version=1.0-SNAPSHOT +package=software.amazonaws.test +service=kinesis +httpClient=netty-nio-client +handlerClassName=MyNettyFunction +region=us-east-1 +javaSdkVersion=2.11.0 +nettyOpenSslVersion=2.0.29.Final + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/goal.txt b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/goal.txt new file mode 100644 index 000000000000..4a1a71d3364c --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/goal.txt @@ -0,0 +1 @@ +verify \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/README.md b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/README.md new file mode 100644 index 000000000000..e265f49bbdc4 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/README.md @@ -0,0 +1,45 @@ +# MyNettyFunction + +This project contains an AWS Lambda maven application with [AWS Java SDK 2.x](https://github.com/aws/aws-sdk-java-v2) dependencies. + +## Prerequisites +- Java 1.8+ +- Apache Maven +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Docker + +## Development + +The generated function handler class just returns the input. The configured AWS Java SDK client is created in `DependencyFactory` class and you can +add the code to interact with the SDK client based on your use case. + +#### Building the project +``` +mvn clean install +``` + +#### Testing it locally +``` +sam local invoke +``` + +#### Adding more SDK clients +To add more service clients, you need to add the specific services modules in `pom.xml` and create the clients in `DependencyFactory` following the same +pattern as kinesisClient. + +## Deployment + +The generated project contains a default [SAM template](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. You might also need to add specific policies to the lambda function +so that it can access other AWS resources. + +To deploy the application, you can run the following command: + +``` +sam deploy --guided +``` + +See [Deploying Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-deploying.html) for more info. + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/pom.xml b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/pom.xml new file mode 100644 index 000000000000..003cb97c99c7 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + software.amazonaws.test + test-netty-artifact + 1.0-SNAPSHOT + jar + + UTF-8 + 1.8 + 1.8 + 3.1.1 + 3.6.1 + 1.6.0 + 2.11.0 + 1.2.0 + 5.4.2 + 2.0.29.Final + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + software.amazon.awssdk + kinesis + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk + netty-nio-client + + + + + io.netty + netty-tcnative-boringssl-static + ${netty.openssl.version} + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + false + test-netty-artifact + + + + package + + shade + + + + + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java new file mode 100644 index 000000000000..ab3f3792bb7e --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java @@ -0,0 +1,26 @@ + +package software.amazonaws.test; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; + +/** + * The module containing all dependencies required by the {@link MyNettyFunction}. + */ +public class DependencyFactory { + + private DependencyFactory() {} + + /** + * @return an instance of KinesisAsyncClient + */ + public static KinesisAsyncClient kinesisClient() { + return KinesisAsyncClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.US_EAST_1) + .httpClientBuilder(NettyNioAsyncHttpClient.builder()) + .build(); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/main/java/software/amazonaws/test/MyNettyFunction.java b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/main/java/software/amazonaws/test/MyNettyFunction.java new file mode 100644 index 000000000000..0016c30c56e0 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/main/java/software/amazonaws/test/MyNettyFunction.java @@ -0,0 +1,28 @@ +package software.amazonaws.test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.kinesis.KinesisAsyncClient; + +/** + * Lambda function entry point. You can change to use other pojo type or implement + * a different RequestHandler. + * + * @see Lambda Java Handler for more information + */ +public class MyNettyFunction implements RequestHandler { + private final KinesisAsyncClient kinesisClient; + + public MyNettyFunction() { + // Initialize the SDK client outside of the handler method so that it can be reused for subsequent invocations. + // It is initialized when the class is loaded. + kinesisClient = DependencyFactory.kinesisClient(); + // Consider invoking a simple api here to pre-warm up the application, eg: dynamodb#listTables + } + + @Override + public Object handleRequest(final Object input, final Context context) { + // TODO: invoking the api call using kinesisClient. + return input; + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/test/java/software/amazonaws/test/MyNettyFunctionTest.java b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/test/java/software/amazonaws/test/MyNettyFunctionTest.java new file mode 100644 index 000000000000..46e9272647aa --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/src/test/java/software/amazonaws/test/MyNettyFunctionTest.java @@ -0,0 +1,15 @@ +package software.amazonaws.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MyNettyFunctionTest { + + @Test + public void handleRequest_shouldReturnConstantValue() { + MyNettyFunction function = new MyNettyFunction(); + Object result = function.handleRequest("echo", null); + assertEquals("echo", result); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/template.yaml b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/template.yaml new file mode 100644 index 000000000000..e674e2599da3 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/nettyclient/reference/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + # See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + # for more info to see how to tune the lambda function configs based on your use case. + MyNettyFunctionFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: java8 + Handler: software.amazonaws.test.MyNettyFunction::handleRequest + Timeout: 60 + MemorySize: 512 + CodeUri: ./target/test-netty-artifact.jar + # Attach policies here to give the function permission to access other AWS resources if needed + # See: https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst + # eg: + #Policies: + # - S3ReadPolicy: + # BucketName: test-bucket \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/archetype.properties b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/archetype.properties new file mode 100644 index 000000000000..4b987f49a274 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/archetype.properties @@ -0,0 +1,10 @@ +groupId=software.amazonaws.test +artifactId=test-url-connection-client-artifact +version=1.0-SNAPSHOT +package=software.amazonaws.test +service=s3 +httpClient=url-connection-client +handlerClassName=App +region=us-west-2 +javaSdkVersion=2.11.0 +nettyOpenSslVersion=2.0.29.Final \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/goal.txt b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/goal.txt new file mode 100644 index 000000000000..4a1a71d3364c --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/goal.txt @@ -0,0 +1 @@ +verify \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/README.md b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/README.md new file mode 100644 index 000000000000..6b17a7840213 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/README.md @@ -0,0 +1,45 @@ +# App + +This project contains an AWS Lambda maven application with [AWS Java SDK 2.x](https://github.com/aws/aws-sdk-java-v2) dependencies. + +## Prerequisites +- Java 1.8+ +- Apache Maven +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Docker + +## Development + +The generated function handler class just returns the input. The configured AWS Java SDK client is created in `DependencyFactory` class and you can +add the code to interact with the SDK client based on your use case. + +#### Building the project +``` +mvn clean install +``` + +#### Testing it locally +``` +sam local invoke +``` + +#### Adding more SDK clients +To add more service clients, you need to add the specific services modules in `pom.xml` and create the clients in `DependencyFactory` following the same +pattern as s3Client. + +## Deployment + +The generated project contains a default [SAM template](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. You might also need to add specific policies to the lambda function +so that it can access other AWS resources. + +To deploy the application, you can run the following command: + +``` +sam deploy --guided +``` + +See [Deploying Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-deploying.html) for more info. + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/pom.xml b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/pom.xml new file mode 100644 index 000000000000..addcc8788bcb --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + software.amazonaws.test + test-url-connection-client-artifact + 1.0-SNAPSHOT + jar + + UTF-8 + 1.8 + 1.8 + 3.1.1 + 3.6.1 + 1.6.0 + 2.11.0 + 1.2.0 + 5.4.2 + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + software.amazon.awssdk + s3 + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk + url-connection-client + + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + false + test-url-connection-client-artifact + + + + package + + shade + + + + + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/main/java/software/amazonaws/test/App.java b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/main/java/software/amazonaws/test/App.java new file mode 100644 index 000000000000..3d3588fee28a --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/main/java/software/amazonaws/test/App.java @@ -0,0 +1,28 @@ +package software.amazonaws.test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.s3.S3Client; + +/** + * Lambda function entry point. You can change to use other pojo type or implement + * a different RequestHandler. + * + * @see Lambda Java Handler for more information + */ +public class App implements RequestHandler { + private final S3Client s3Client; + + public App() { + // Initialize the SDK client outside of the handler method so that it can be reused for subsequent invocations. + // It is initialized when the class is loaded. + s3Client = DependencyFactory.s3Client(); + // Consider invoking a simple api here to pre-warm up the application, eg: dynamodb#listTables + } + + @Override + public Object handleRequest(final Object input, final Context context) { + // TODO: invoking the api call using s3Client. + return input; + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java new file mode 100644 index 000000000000..926269195a94 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java @@ -0,0 +1,26 @@ + +package software.amazonaws.test; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +/** + * The module containing all dependencies required by the {@link App}. + */ +public class DependencyFactory { + + private DependencyFactory() {} + + /** + * @return an instance of S3Client + */ + public static S3Client s3Client() { + return S3Client.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.US_WEST_2) + .httpClientBuilder(UrlConnectionHttpClient.builder()) + .build(); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/test/java/software/amazonaws/test/AppTest.java b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/test/java/software/amazonaws/test/AppTest.java new file mode 100644 index 000000000000..8400b37496b7 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/src/test/java/software/amazonaws/test/AppTest.java @@ -0,0 +1,15 @@ +package software.amazonaws.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class AppTest { + + @Test + public void handleRequest_shouldReturnConstantValue() { + App function = new App(); + Object result = function.handleRequest("echo", null); + assertEquals("echo", result); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/template.yaml b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/template.yaml new file mode 100644 index 000000000000..ca0bb619fd4e --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/urlhttpclient/reference/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + # See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + # for more info to see how to tune the lambda function configs based on your use case. + AppFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: java8 + Handler: software.amazonaws.test.App::handleRequest + Timeout: 60 + MemorySize: 512 + CodeUri: ./target/test-url-connection-client-artifact.jar + # Attach policies here to give the function permission to access other AWS resources if needed + # See: https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst + # eg: + #Policies: + # - S3ReadPolicy: + # BucketName: test-bucket \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/archetype.properties b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/archetype.properties new file mode 100644 index 000000000000..de95a87a7f91 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/archetype.properties @@ -0,0 +1,25 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file 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. +# + +groupId=software.amazonaws.test +artifactId=test-wafregional-artifact +version=1.0-SNAPSHOT +package=software.amazonaws.test +service=wafregional +httpClient=apache-client +handlerClassName=MyWafRegionalFunction +region=ap-southeast-1 +javaSdkVersion=2.11.0 +nettyOpenSslVersion=2.0.29.Final \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/goal.txt b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/goal.txt new file mode 100644 index 000000000000..4a1a71d3364c --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/goal.txt @@ -0,0 +1 @@ +verify \ No newline at end of file diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/README.md b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/README.md new file mode 100644 index 000000000000..cdff80c819bd --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/README.md @@ -0,0 +1,45 @@ +# MyWafRegionalFunction + +This project contains an AWS Lambda maven application with [AWS Java SDK 2.x](https://github.com/aws/aws-sdk-java-v2) dependencies. + +## Prerequisites +- Java 1.8+ +- Apache Maven +- [AWS SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) +- Docker + +## Development + +The generated function handler class just returns the input. The configured AWS Java SDK client is created in `DependencyFactory` class and you can +add the code to interact with the SDK client based on your use case. + +#### Building the project +``` +mvn clean install +``` + +#### Testing it locally +``` +sam local invoke +``` + +#### Adding more SDK clients +To add more service clients, you need to add the specific services modules in `pom.xml` and create the clients in `DependencyFactory` following the same +pattern as wafRegionalClient. + +## Deployment + +The generated project contains a default [SAM template](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html) file `template.yaml` where you can +configure different properties of your lambda function such as memory size and timeout. You might also need to add specific policies to the lambda function +so that it can access other AWS resources. + +To deploy the application, you can run the following command: + +``` +sam deploy --guided +``` + +See [Deploying Serverless Applications](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-deploying.html) for more info. + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/pom.xml b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/pom.xml new file mode 100644 index 000000000000..68fe5c93a944 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + software.amazonaws.test + test-wafregional-artifact + 1.0-SNAPSHOT + jar + + UTF-8 + 1.8 + 1.8 + 3.1.1 + 3.6.1 + 1.6.0 + 2.11.0 + 1.2.0 + 5.4.2 + + + + + + software.amazon.awssdk + bom + ${aws.java.sdk.version} + pom + import + + + + + + + software.amazon.awssdk + waf + + + software.amazon.awssdk + netty-nio-client + + + software.amazon.awssdk + apache-client + + + + + + software.amazon.awssdk + apache-client + + + + com.amazonaws + aws-lambda-java-core + ${aws.lambda.java.version} + + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.plugin.version} + + false + test-wafregional-artifact + + + + package + + shade + + + + + + + diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java new file mode 100644 index 000000000000..31bacbfa33ef --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/main/java/software/amazonaws/test/DependencyFactory.java @@ -0,0 +1,26 @@ + +package software.amazonaws.test; + +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.waf.regional.WafRegionalClient; + +/** + * The module containing all dependencies required by the {@link MyWafRegionalFunction}. + */ +public class DependencyFactory { + + private DependencyFactory() {} + + /** + * @return an instance of WafRegionalClient + */ + public static WafRegionalClient wafRegionalClient() { + return WafRegionalClient.builder() + .credentialsProvider(EnvironmentVariableCredentialsProvider.create()) + .region(Region.AP_SOUTHEAST_1) + .httpClientBuilder(ApacheHttpClient.builder()) + .build(); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/main/java/software/amazonaws/test/MyWafRegionalFunction.java b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/main/java/software/amazonaws/test/MyWafRegionalFunction.java new file mode 100644 index 000000000000..9f02b456977a --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/main/java/software/amazonaws/test/MyWafRegionalFunction.java @@ -0,0 +1,28 @@ +package software.amazonaws.test; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import software.amazon.awssdk.services.waf.regional.WafRegionalClient; + +/** + * Lambda function entry point. You can change to use other pojo type or implement + * a different RequestHandler. + * + * @see Lambda Java Handler for more information + */ +public class MyWafRegionalFunction implements RequestHandler { + private final WafRegionalClient wafRegionalClient; + + public MyWafRegionalFunction() { + // Initialize the SDK client outside of the handler method so that it can be reused for subsequent invocations. + // It is initialized when the class is loaded. + wafRegionalClient = DependencyFactory.wafRegionalClient(); + // Consider invoking a simple api here to pre-warm up the application, eg: dynamodb#listTables + } + + @Override + public Object handleRequest(final Object input, final Context context) { + // TODO: invoking the api call using wafRegionalClient. + return input; + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/test/java/software/amazonaws/test/MyWafRegionalFunctionTest.java b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/test/java/software/amazonaws/test/MyWafRegionalFunctionTest.java new file mode 100644 index 000000000000..adc0157faf76 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/src/test/java/software/amazonaws/test/MyWafRegionalFunctionTest.java @@ -0,0 +1,15 @@ +package software.amazonaws.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MyWafRegionalFunctionTest { + + @Test + public void handleRequest_shouldReturnConstantValue() { + MyWafRegionalFunction function = new MyWafRegionalFunction(); + Object result = function.handleRequest("echo", null); + assertEquals("echo", result); + } +} diff --git a/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/template.yaml b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/template.yaml new file mode 100644 index 000000000000..70ee17fae8a3 --- /dev/null +++ b/archetypes/archetype-lambda/src/test/resources/projects/wafregionalclient/reference/template.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Resources: + # See https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html + # for more info to see how to tune the lambda function configs based on your use case. + MyWafRegionalFunctionFunction: + Type: AWS::Serverless::Function + Properties: + Runtime: java8 + Handler: software.amazonaws.test.MyWafRegionalFunction::handleRequest + Timeout: 60 + MemorySize: 512 + CodeUri: ./target/test-wafregional-artifact.jar + # Attach policies here to give the function permission to access other AWS resources if needed + # See: https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst + # eg: + #Policies: + # - S3ReadPolicy: + # BucketName: test-bucket \ No newline at end of file diff --git a/archetypes/pom.xml b/archetypes/pom.xml new file mode 100644 index 000000000000..c0b3b6bc9e01 --- /dev/null +++ b/archetypes/pom.xml @@ -0,0 +1,35 @@ + + + + + + aws-sdk-java-pom + software.amazon.awssdk + 2.11.8-SNAPSHOT + + 4.0.0 + archetypes + AWS Java SDK :: Archetypes + + archetype-lambda + + pom + + Maven Archetypes for applications using Java SDK 2.x + + \ No newline at end of file diff --git a/aws-sdk-java/build.properties b/aws-sdk-java/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/aws-sdk-java/build.properties +++ b/aws-sdk-java/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/aws-sdk-java/pom.xml b/aws-sdk-java/pom.xml index 767c3a8d19a9..05f3c137f192 100644 --- a/aws-sdk-java/pom.xml +++ b/aws-sdk-java/pom.xml @@ -1,10 +1,25 @@ + + 4.0.0 software.amazon.awssdk aws-sdk-java-pom - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT ../pom.xml aws-sdk-java @@ -955,6 +970,146 @@ Amazon AutoScaling, etc). workmailmessageflow ${awsjavasdk.version} + + software.amazon.awssdk + codestarnotifications + ${awsjavasdk.version} + + + software.amazon.awssdk + savingsplans + ${awsjavasdk.version} + + + software.amazon.awssdk + sso + ${awsjavasdk.version} + + + software.amazon.awssdk + ssooidc + ${awsjavasdk.version} + + + software.amazon.awssdk + marketplacecatalog + ${awsjavasdk.version} + + + software.amazon.awssdk + sesv2 + ${awsjavasdk.version} + + + software.amazon.awssdk + dataexchange + ${awsjavasdk.version} + + + software.amazon.awssdk + migrationhubconfig + ${awsjavasdk.version} + + + software.amazon.awssdk + connectparticipant + ${awsjavasdk.version} + + + software.amazon.awssdk + wafv2 + ${awsjavasdk.version} + + + software.amazon.awssdk + appconfig + ${awsjavasdk.version} + + + software.amazon.awssdk + iotsecuretunneling + ${awsjavasdk.version} + + + software.amazon.awssdk + elasticinference + ${awsjavasdk.version} + + + software.amazon.awssdk + imagebuilder + ${awsjavasdk.version} + + + software.amazon.awssdk + schemas + ${awsjavasdk.version} + + + software.amazon.awssdk + accessanalyzer + ${awsjavasdk.version} + + + software.amazon.awssdk + computeoptimizer + ${awsjavasdk.version} + + + software.amazon.awssdk + networkmanager + ${awsjavasdk.version} + + + software.amazon.awssdk + kendra + ${awsjavasdk.version} + + + software.amazon.awssdk + frauddetector + ${awsjavasdk.version} + + + software.amazon.awssdk + codegurureviewer + ${awsjavasdk.version} + + + software.amazon.awssdk + codeguruprofiler + ${awsjavasdk.version} + + + software.amazon.awssdk + outposts + ${awsjavasdk.version} + + + software.amazon.awssdk + sagemakera2iruntime + ${awsjavasdk.version} + + + software.amazon.awssdk + ebs + ${awsjavasdk.version} + + + software.amazon.awssdk + kinesisvideosignaling + ${awsjavasdk.version} + + + software.amazon.awssdk + detective + ${awsjavasdk.version} + + + software.amazon.awssdk + codestarconnections + ${awsjavasdk.version} + ${project.artifactId}-${project.version} diff --git a/bom-internal/pom.xml b/bom-internal/pom.xml index c6c805cfb0b7..d174d998a091 100644 --- a/bom-internal/pom.xml +++ b/bom-internal/pom.xml @@ -1,11 +1,26 @@ + + aws-sdk-java-pom software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 @@ -186,7 +201,7 @@ com.puppycrawl.tools checkstyle - 8.7 + 8.29 org.apache.maven.plugins @@ -285,6 +300,12 @@ ${junit.version} test + + org.testng + testng + ${testng.version} + test + org.hamcrest hamcrest-all diff --git a/bom/build.properties b/bom/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/bom/build.properties +++ b/bom/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/bom/pom.xml b/bom/pom.xml index 4355c871c1b0..d0756c704b7d 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -1,10 +1,25 @@ + + 4.0.0 software.amazon.awssdk aws-sdk-java-pom - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT ../pom.xml bom @@ -44,6 +59,11 @@ annotations ${awsjavasdk.version} + + software.amazon.awssdk + arns + ${awsjavasdk.version} + software.amazon.awssdk auth @@ -1065,6 +1085,146 @@ workmailmessageflow ${awsjavasdk.version} + + software.amazon.awssdk + codestarnotifications + ${awsjavasdk.version} + + + software.amazon.awssdk + savingsplans + ${awsjavasdk.version} + + + software.amazon.awssdk + sso + ${awsjavasdk.version} + + + software.amazon.awssdk + ssooidc + ${awsjavasdk.version} + + + software.amazon.awssdk + marketplacecatalog + ${awsjavasdk.version} + + + software.amazon.awssdk + sesv2 + ${awsjavasdk.version} + + + software.amazon.awssdk + dataexchange + ${awsjavasdk.version} + + + software.amazon.awssdk + migrationhubconfig + ${awsjavasdk.version} + + + software.amazon.awssdk + connectparticipant + ${awsjavasdk.version} + + + software.amazon.awssdk + wafv2 + ${awsjavasdk.version} + + + software.amazon.awssdk + appconfig + ${awsjavasdk.version} + + + software.amazon.awssdk + iotsecuretunneling + ${awsjavasdk.version} + + + software.amazon.awssdk + elasticinference + ${awsjavasdk.version} + + + software.amazon.awssdk + imagebuilder + ${awsjavasdk.version} + + + software.amazon.awssdk + schemas + ${awsjavasdk.version} + + + software.amazon.awssdk + accessanalyzer + ${awsjavasdk.version} + + + software.amazon.awssdk + computeoptimizer + ${awsjavasdk.version} + + + software.amazon.awssdk + networkmanager + ${awsjavasdk.version} + + + software.amazon.awssdk + kendra + ${awsjavasdk.version} + + + software.amazon.awssdk + frauddetector + ${awsjavasdk.version} + + + software.amazon.awssdk + codegurureviewer + ${awsjavasdk.version} + + + software.amazon.awssdk + codeguruprofiler + ${awsjavasdk.version} + + + software.amazon.awssdk + outposts + ${awsjavasdk.version} + + + software.amazon.awssdk + sagemakera2iruntime + ${awsjavasdk.version} + + + software.amazon.awssdk + ebs + ${awsjavasdk.version} + + + software.amazon.awssdk + kinesisvideosignaling + ${awsjavasdk.version} + + + software.amazon.awssdk + detective + ${awsjavasdk.version} + + + software.amazon.awssdk + codestarconnections + ${awsjavasdk.version} + diff --git a/build-tools/pom.xml b/build-tools/pom.xml index 58ebb2a6a409..d19195ce8964 100644 --- a/build-tools/pom.xml +++ b/build-tools/pom.xml @@ -1,6 +1,6 @@ + value="/*\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the "License").\n * You may not use this file except in compliance with the License.\n * A copy of the License is located at\n *\n * http://aws.amazon.com/apache2.0\n *\n * or in the "license" file accompanying this file. This file is distributed\n * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n * express or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */"/> @@ -69,12 +69,6 @@ - - - - - - @@ -386,4 +380,11 @@ + + + + + + + diff --git a/build-tools/src/main/resources/software/amazon/awssdk/intellij-codestyle.xml b/build-tools/src/main/resources/software/amazon/awssdk/intellij-codestyle.xml index e6e6e5ef6a68..3e7114b50570 100644 --- a/build-tools/src/main/resources/software/amazon/awssdk/intellij-codestyle.xml +++ b/build-tools/src/main/resources/software/amazon/awssdk/intellij-codestyle.xml @@ -1,5 +1,5 @@ + - diff --git a/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml b/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml index 69f864b741af..d1809a003d7d 100644 --- a/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml +++ b/build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml @@ -1,6 +1,6 @@ + + + + + + + diff --git a/buildspecs/integ-test.yml b/buildspecs/integ-test.yml index ff4a67b98e8e..9b6260b8bc9a 100644 --- a/buildspecs/integ-test.yml +++ b/buildspecs/integ-test.yml @@ -7,7 +7,7 @@ phases: build: commands: - - mvn clean install -Dskip.unit.tests -P integration-tests -Dfindbugs.skip -Dcheckstyle.skip -pl !:dynamodbdocument-v1,!:dynamodbmapper-v1 -T1C + - mvn clean install -Dskip.unit.tests -P integration-tests -Dfindbugs.skip -Dcheckstyle.skip -T1C - JAVA_VERSION=$(java -version 2>&1 | grep -i version | cut -d'"' -f2 | cut -d'.' -f1-1) - echo $JAVA_VERSION - | diff --git a/buildspecs/on-demand-integ-test.yml b/buildspecs/on-demand-integ-test.yml index 72889bad78db..29e21c8777df 100644 --- a/buildspecs/on-demand-integ-test.yml +++ b/buildspecs/on-demand-integ-test.yml @@ -7,7 +7,7 @@ phases: build: commands: - - mvn clean install -Dskip.unit.tests -P integration-tests -Dfindbugs.skip -Dcheckstyle.skip -pl !:dynamodbmapper-v1 -Dfailsafe.rerunFailingTestsCount=1 --fail-at-end + - mvn clean install -Dskip.unit.tests -P integration-tests -Dfindbugs.skip -Dcheckstyle.skip -Dfailsafe.rerunFailingTestsCount=1 --fail-at-end - JAVA_VERSION=$(java -version 2>&1 | grep -i version | cut -d'"' -f2 | cut -d'.' -f1-1) - echo $JAVA_VERSION - | diff --git a/buildspecs/release-javadoc.yml b/buildspecs/release-javadoc.yml index ebdbdbe0979f..0c2040e3975e 100644 --- a/buildspecs/release-javadoc.yml +++ b/buildspecs/release-javadoc.yml @@ -15,7 +15,7 @@ phases: build: commands: - mvn install -P quick -T1C - - mvn install javadoc:aggregate -B -Ppublic-javadoc -Dcheckstyle.skip -Dspotbugs.skip -DskipTests -Ddoclint=none -pl '!:dynamodbdocument-v1,!:dynamodbmapper-v1,!:protocol-tests,!:protocol-tests-core,!:codegen-generated-classes-test,!:sdk-benchmarks,!:module-path-tests,!:test-utils,!:http-client-tests,!:tests-coverage-reporting' + - mvn install javadoc:aggregate -B -Ppublic-javadoc -Dcheckstyle.skip -Dspotbugs.skip -DskipTests -Ddoclint=none -pl '!:protocol-tests,!:protocol-tests-core,!:codegen-generated-classes-test,!:sdk-benchmarks,!:module-path-tests,!:test-utils,!:http-client-tests,!:tests-coverage-reporting' - RELEASE_VERSION=`mvn -q -Dexec.executable=echo -Dexec.args='${project.version}' --non-recursive exec:exec` - - aws s3 sync target/site/apidocs/ $DOC_PATH/$RELEASE_VERSION/ diff --git a/buildspecs/release-to-maven.yml b/buildspecs/release-to-maven.yml index 1fa534c1e158..f419e07d75b6 100644 --- a/buildspecs/release-to-maven.yml +++ b/buildspecs/release-to-maven.yml @@ -20,7 +20,7 @@ phases: if ! curl -f --head $SONATYPE_URL; then mkdir -p $CREDENTIALS aws s3 cp s3://aws-java-sdk-release-credentials/ $CREDENTIALS/ --recursive - mvn clean deploy -B -s $SETTINGS_XML -Dgpg.homedir=$GPG_HOME -Ppublishing -DperformRelease -Dspotbugs.skip -DskipTests -Dcheckstyle.skip -Ddoclint=none -pl !:dynamodbdocument-v1,!:dynamodbmapper-v1,!:protocol-tests,!:protocol-tests-core,!:codegen-generated-classes-test,!:sdk-benchmarks,!:module-path-tests,!:tests-coverage-reporting,!:stability-tests -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30 + mvn clean deploy -B -s $SETTINGS_XML -Dgpg.homedir=$GPG_HOME -Ppublishing -DperformRelease -Dspotbugs.skip -DskipTests -Dcheckstyle.skip -Ddoclint=none -pl !:protocol-tests,!:protocol-tests-core,!:codegen-generated-classes-test,!:sdk-benchmarks,!:module-path-tests,!:tests-coverage-reporting,!:stability-tests -DautoReleaseAfterClose=true -DstagingProgressTimeoutMinutes=30 else echo "This version was already released." fi diff --git a/bundle/build.properties b/bundle/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/bundle/build.properties +++ b/bundle/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/bundle/pom.xml b/bundle/pom.xml index fa43c3a5b293..754b81f93d99 100644 --- a/bundle/pom.xml +++ b/bundle/pom.xml @@ -1,6 +1,6 @@ + @@ -7,7 +22,7 @@ software.amazon.awssdk aws-sdk-java-pom - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT ../pom.xml codegen-lite-maven-plugin diff --git a/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java b/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java index 94d10946405b..ceee556742d1 100644 --- a/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java +++ b/codegen-lite-maven-plugin/src/main/java/software/amazon/awssdk/codegen/lite/maven/plugin/RegionGenerationMojo.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -92,13 +92,9 @@ public void generateRegionClass(Path baseSourcesDirectory, Partitions partitions public void generateServiceMetadata(Path baseSourcesDirectory, Partitions partitions) { Path sourcesDirectory = baseSourcesDirectory.resolve(SERVICE_METADATA_BASE.replace(".", "/")); Set services = new HashSet<>(); - partitions.getPartitions().stream().forEach(p -> services.addAll(p.getServices().keySet())); + partitions.getPartitions().forEach(p -> services.addAll(p.getServices().keySet())); - services.stream() - // Use hardcoded file for elasticache until the incorrect fips endpoint is fixed - //TODO Remove once elasticache endpoints are fixed at source - .filter(s -> !"elasticache".equals(s)) - .forEach(s -> new CodeGenerator(sourcesDirectory.toString(), new ServiceMetadataGenerator(partitions, + services.forEach(s -> new CodeGenerator(sourcesDirectory.toString(), new ServiceMetadataGenerator(partitions, s, SERVICE_METADATA_BASE, REGION_BASE)) diff --git a/codegen-lite/pom.xml b/codegen-lite/pom.xml index 8fff06cd6052..a0cb1447a45f 100644 --- a/codegen-lite/pom.xml +++ b/codegen-lite/pom.xml @@ -1,4 +1,19 @@ + + @@ -6,7 +21,7 @@ software.amazon.awssdk aws-sdk-java-pom - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT codegen-lite AWS Java SDK :: Code Generator Lite diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/CodeGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/CodeGenerator.java index 6238e07c7a52..56c6798c6a80 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/CodeGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/CodeGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/PoetClass.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/PoetClass.java index 2769d71ae266..d38f0593c615 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/PoetClass.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/PoetClass.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/Utils.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/Utils.java index 371e38e6a690..2e12c8ca10c1 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/Utils.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeTransformer.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeTransformer.java index c98cffc6609c..056b5eadff42 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeTransformer.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeWriter.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeWriter.java index 86c60b76bd25..4a2bb49277db 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeWriter.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/CodeWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/JavaCodeFormatter.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/JavaCodeFormatter.java index 4ffd3cd0891f..e4ac3581dcf3 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/JavaCodeFormatter.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/JavaCodeFormatter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/LinkRemover.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/LinkRemover.java index 35f5cbab6230..d1955ad5ae17 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/LinkRemover.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/LinkRemover.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/UnusedImportRemover.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/UnusedImportRemover.java index c4bf9616b0cc..a2410d38dce6 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/UnusedImportRemover.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/emitters/UnusedImportRemover.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataGenerator.java index 25ad7b565336..5f0f505a4e89 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataProviderGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataProviderGenerator.java index d5b67c3a5cf0..4555dd209725 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataProviderGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/PartitionMetadataProviderGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -82,7 +82,6 @@ private CodeBlock partitions(Partitions partitions) { CodeBlock.Builder builder = CodeBlock.builder().add("$T.builder()", ImmutableMap.class); partitions.getPartitions() - .stream() .forEach(p -> builder.add(".put($S, new $T())", p.getPartition(), partitionMetadataClass(p.getPartition()))); return builder.add(".build()").build(); diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerator.java index f50f71fb5df8..fa5467bc847f 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -109,7 +109,12 @@ private void regions(TypeSpec.Builder builder) { addGlobalRegions(builder); - regionsArray.add(regionsCodeBlock + ", ").add("AWS_GLOBAL, ").add("AWS_CN_GLOBAL, ").add("AWS_US_GOV_GLOBAL"); + regionsArray.add(regionsCodeBlock + ", ") + .add("AWS_GLOBAL, ") + .add("AWS_CN_GLOBAL, ") + .add("AWS_US_GOV_GLOBAL, ") + .add("AWS_ISO_GLOBAL, ") + .add("AWS_ISO_B_GLOBAL"); regionsArray.add("))"); TypeName listOfRegions = ParameterizedTypeName.get(ClassName.get(List.class), className()); @@ -130,6 +135,14 @@ private void addGlobalRegions(TypeSpec.Builder builder) { .addField(FieldSpec.builder(className(), "AWS_US_GOV_GLOBAL") .addModifiers(PUBLIC, STATIC, FINAL) .initializer("$T.of($S, true)", className(), "aws-us-gov-global") + .build()) + .addField(FieldSpec.builder(className(), "AWS_ISO_GLOBAL") + .addModifiers(PUBLIC, STATIC, FINAL) + .initializer("$T.of($S, true)", className(), "aws-iso-global") + .build()) + .addField(FieldSpec.builder(className(), "AWS_ISO_B_GLOBAL") + .addModifiers(PUBLIC, STATIC, FINAL) + .initializer("$T.of($S, true)", className(), "aws-iso-b-global") .build()); } diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataGenerator.java index a1cfe810f271..92011139d2c6 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataLoader.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataLoader.java index 366414225840..984cc6d930b5 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataLoader.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataProviderGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataProviderGenerator.java index 3c49b91a3871..0203bbbfb649 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataProviderGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionMetadataProviderGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -83,7 +83,6 @@ private CodeBlock regions(Partitions partitions) { CodeBlock.Builder builder = CodeBlock.builder().add("$T.builder()", ImmutableMap.class); partitions.getPartitions() - .stream() .forEach(p -> p.getRegions() .keySet() .forEach(r -> builder.add(".put(Region.$L, new $T())", regionClass(r), regionMetadataClass(r)))); diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtil.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtil.java index 65f2389b839d..63343bea5ddf 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtil.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -24,7 +24,8 @@ public final class RegionValidationUtil { private static final String FIPS_PREFIX = "fips-"; - private RegionValidationUtil() {} + private RegionValidationUtil() { + } /** * Determines if a given region string is a "valid" AWS region. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataGenerator.java index 3bb16edab5ce..d00774ef1d5a 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -50,16 +50,16 @@ public class ServiceMetadataGenerator implements PoetClass { private final Partitions partitions; - private final String service; + private final String serviceEndpointPrefix; private final String basePackage; private final String regionBasePackage; public ServiceMetadataGenerator(Partitions partitions, - String service, + String serviceEndpointPrefix, String basePackage, String regionBasePackage) { this.partitions = partitions; - this.service = service; + this.serviceEndpointPrefix = serviceEndpointPrefix; this.basePackage = basePackage; this.regionBasePackage = regionBasePackage; } @@ -68,6 +68,8 @@ public ServiceMetadataGenerator(Partitions partitions, public TypeSpec poetClass() { TypeName listOfRegions = ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get(regionBasePackage, "Region")); TypeName mapOfStringString = ParameterizedTypeName.get(Map.class, String.class, String.class); + TypeName listOfServicePartitionMetadata = + ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get(regionBasePackage, "ServicePartitionMetadata")); return TypeSpec.classBuilder(className()) .addModifiers(Modifier.PUBLIC) @@ -79,7 +81,7 @@ public TypeSpec poetClass() { .addSuperinterface(ClassName.get(regionBasePackage, "ServiceMetadata")) .addField(FieldSpec.builder(String.class, "ENDPOINT_PREFIX") .addModifiers(PRIVATE, FINAL, STATIC) - .initializer("$S", service) + .initializer("$S", serviceEndpointPrefix) .build()) .addField(FieldSpec.builder(mapOfStringString, "PARTITION_OVERRIDDEN_ENDPOINTS") .addModifiers(PRIVATE, FINAL, STATIC) @@ -97,15 +99,20 @@ public TypeSpec poetClass() { .addModifiers(PRIVATE, FINAL, STATIC) .initializer(signingRegionOverrides(partitions)) .build()) + .addField(FieldSpec.builder(listOfServicePartitionMetadata, "PARTITIONS") + .addModifiers(PRIVATE, FINAL, STATIC) + .initializer(servicePartitions(partitions)) + .build()) .addMethod(regions()) .addMethod(endpointFor()) .addMethod(signingRegion()) + .addMethod(partitions(listOfServicePartitionMetadata)) .build(); } @Override public ClassName className() { - String sanitizedServiceName = service.replace(".", "-"); + String sanitizedServiceName = serviceEndpointPrefix.replace(".", "-"); return ClassName.get(basePackage, Stream.of(sanitizedServiceName.split("-")) .map(Utils::capitalize) .collect(Collectors.joining()) + "ServiceMetadata"); @@ -131,7 +138,6 @@ private CodeBlock serviceEndpoints(Partitions partitions) { CodeBlock.Builder builder = CodeBlock.builder().add("$T.builder()", ImmutableMap.class); services.entrySet() - .stream() .forEach(s -> s.getValue().getEndpoints() .entrySet() .stream() @@ -151,8 +157,8 @@ private CodeBlock regionsField(Partitions partitions) { partitions.getPartitions() .stream() - .filter(p -> p.getServices().containsKey(service)) - .forEach(p -> regions.addAll(p.getServices().get(service).getEndpoints().keySet() + .filter(p -> p.getServices().containsKey(serviceEndpointPrefix)) + .forEach(p -> regions.addAll(p.getServices().get(serviceEndpointPrefix).getEndpoints().keySet() .stream() .filter(r -> RegionValidationUtil.validRegion(r, p.getRegionRegex())) .collect(Collectors.toList()))); @@ -173,7 +179,6 @@ private CodeBlock signingRegionOverrides(Partitions partitions) { CodeBlock.Builder builder = CodeBlock.builder().add("$T.builder()", ImmutableMap.class); serviceData.entrySet() - .stream() .forEach(s -> s.getValue().getEndpoints() .entrySet() .stream() @@ -187,6 +192,39 @@ private CodeBlock signingRegionOverrides(Partitions partitions) { return builder.add(".build()").build(); } + private CodeBlock servicePartitions(Partitions partitions) { + return CodeBlock.builder() + .add("$T.unmodifiableList($T.asList(", Collections.class, Arrays.class) + .add(commaSeparatedServicePartitions(partitions)) + .add("))") + .build(); + } + + private CodeBlock commaSeparatedServicePartitions(Partitions partitions) { + ClassName defaultServicePartitionMetadata = ClassName.get(regionBasePackage + ".internal", + "DefaultServicePartitionMetadata"); + return partitions.getPartitions() + .stream() + .filter(p -> p.getServices().containsKey(serviceEndpointPrefix)) + .map(p -> CodeBlock.of("new $T($S, $L)", + defaultServicePartitionMetadata, + p.getPartition(), + globalRegion(p))) + .collect(CodeBlock.joining(",")); + } + + private CodeBlock globalRegion(Partition partition) { + ClassName region = ClassName.get(regionBasePackage, "Region"); + Service service = partition.getServices().get(this.serviceEndpointPrefix); + boolean hasGlobalRegionForPartition = service.isRegionalized() != null && + !service.isRegionalized() && + service.isPartitionWideEndpointAvailable(); + String globalRegionForPartition = hasGlobalRegionForPartition ? service.getPartitionEndpoint() : null; + return globalRegionForPartition == null + ? CodeBlock.of("null") + : CodeBlock.of("$T.of($S)", region, globalRegionForPartition); + } + private MethodSpec regions() { TypeName listOfRegions = ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get(regionBasePackage, "Region")); @@ -221,15 +259,23 @@ private MethodSpec signingRegion() { .build(); } + private MethodSpec partitions(TypeName listOfServicePartitionMetadata) { + return MethodSpec.methodBuilder("servicePartitions") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns(listOfServicePartitionMetadata) + .addStatement("return $L", "PARTITIONS") + .build(); + } + private Map getServiceData(Partitions partitions) { Map serviceData = new TreeMap<>(Comparator.comparing(Partition::getPartition)); partitions.getPartitions() - .stream() .forEach(p -> p.getServices() .entrySet() .stream() - .filter(s -> s.getKey().equalsIgnoreCase(service)) + .filter(s -> s.getKey().equalsIgnoreCase(serviceEndpointPrefix)) .forEach(s -> serviceData.put(p, s.getValue()))); return serviceData; diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java index 395891000fcd..f169fac71f7a 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataProviderGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -86,10 +86,8 @@ private CodeBlock regions(Partitions partitions) { Set seenServices = new HashSet<>(); partitions.getPartitions() - .stream() .forEach(p -> p.getServices() .keySet() - .stream() .forEach(s -> { if (!seenServices.contains(s)) { builder.add(".put($S, new $T())", s, serviceMetadataClass(s)); @@ -101,6 +99,10 @@ private CodeBlock regions(Partitions partitions) { } private ClassName serviceMetadataClass(String service) { + if ("s3".equals(service)) { + // This class contains extra logic for detecting the regional endpoint flag + return ClassName.get(basePackage, "EnhancedS3ServiceMetadata"); + } String sanitizedServiceName = service.replace(".", "-"); return ClassName.get(basePackage, Stream.of(sanitizedServiceName.split("-")) .map(Utils::capitalize) diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/CredentialScope.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/CredentialScope.java index 341c6c19418e..d34bc65d319a 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/CredentialScope.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/CredentialScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Endpoint.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Endpoint.java index 8c89a3e29f76..ace3435126ec 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Endpoint.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Endpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -54,7 +54,8 @@ public final class Endpoint implements Cloneable { */ private String sslCommonName; - public Endpoint() {} + public Endpoint() { + } /** * Merges the given endpoints and returns the merged one. diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partition.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partition.java index b457a97d3a9e..e225d47bfd82 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partition.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partition.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -62,7 +62,8 @@ public final class Partition { */ private Endpoint defaults; - public Partition() {} + public Partition() { + } public Partition(@JsonProperty(value = "partition") String partition, @JsonProperty(value = "regions") Map diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/PartitionRegion.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/PartitionRegion.java index f437b3aa167b..1d4b7d4bdf0d 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/PartitionRegion.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/PartitionRegion.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -30,7 +30,8 @@ public final class PartitionRegion { */ private String description; - public PartitionRegion() {} + public PartitionRegion() { + } public PartitionRegion(@JsonProperty(value = "description") String description) { this.description = Validate.notNull(description, "Region description"); diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partitions.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partitions.java index 074456022064..c0d5dd4c1b12 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partitions.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Partitions.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -36,7 +36,8 @@ public final class Partitions { */ private List partitions; - public Partitions() {} + public Partitions() { + } public Partitions(@JsonProperty(value = "version") String version, @JsonProperty(value = "partitions") List partitions) { diff --git a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Service.java b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Service.java index db89355e5a83..4a95600c5416 100644 --- a/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Service.java +++ b/codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/model/Service.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -45,9 +45,10 @@ public final class Service { /** * Returns true if the service is regionalized. */ - private boolean isRegionalized; + private Boolean isRegionalized; - public Service() {} + public Service() { + } public Service(@JsonProperty(value = "endpoints") Map endpoints) { this.endpoints = Validate.paramNotNull(endpoints, "endpoints"); @@ -99,7 +100,7 @@ public void setPartitionEndpoint(String partitionEndpoint) { /** * returns true if the service is regionalized. */ - public boolean isRegionalized() { + public Boolean isRegionalized() { return isRegionalized; } @@ -107,7 +108,7 @@ public boolean isRegionalized() { * sets the regionalized property for a service.. */ @JsonProperty(value = "isRegionalized") - public void setIsRegionalized(boolean regionalized) { + public void setIsRegionalized(Boolean regionalized) { isRegionalized = regionalized; } diff --git a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/PoetMatchers.java b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/PoetMatchers.java index 727009dec96f..e7c9ed5d2ee5 100644 --- a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/PoetMatchers.java +++ b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/PoetMatchers.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java index b3c214c8fdcb..223ec4d50532 100644 --- a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java +++ b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionGenerationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -26,7 +26,7 @@ public class RegionGenerationTest { - private static final String ENDPOINTS = "/software/amazon/awssdk/codegen/lite/endpoints.json"; + private static final String ENDPOINTS = "/software/amazon/awssdk/codegen/lite/test-endpoints.json"; private static final String SERVICE_METADATA_BASE = "software.amazon.awssdk.regions.servicemetadata"; private static final String REGION_METADATA_BASE = "software.amazon.awssdk.regions.regionmetadata"; private static final String PARTITION_METADATA_BASE = "software.amazon.awssdk.regions.partitionmetadata"; diff --git a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtilTest.java b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtilTest.java index 497669e8ef39..80c47e3c2f90 100644 --- a/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtilTest.java +++ b/codegen-lite/src/test/java/software/amazon/awssdk/codegen/lite/regions/RegionValidationUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/regions.java b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/regions.java index 7d0adb1f49d2..6b3750874bd6 100644 --- a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/regions.java +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/regions.java @@ -80,10 +80,14 @@ public final class Region { public static final Region AWS_US_GOV_GLOBAL = Region.of("aws-us-gov-global", true); + public static final Region AWS_ISO_GLOBAL = Region.of("aws-iso-global", true); + + public static final Region AWS_ISO_B_GLOBAL = Region.of("aws-iso-b-global", true); + private static final List REGIONS = Collections.unmodifiableList(Arrays.asList(AP_SOUTH_1, EU_WEST_3, EU_WEST_2, - EU_WEST_1, AP_NORTHEAST_3, AP_NORTHEAST_2, AP_NORTHEAST_1, CA_CENTRAL_1, SA_EAST_1, CN_NORTH_1, US_GOV_WEST_1, - AP_SOUTHEAST_1, AP_SOUTHEAST_2, EU_CENTRAL_1, US_EAST_1, US_EAST_2, US_WEST_1, CN_NORTHWEST_1, US_WEST_2, AWS_GLOBAL, - AWS_CN_GLOBAL, AWS_US_GOV_GLOBAL)); + EU_WEST_1, AP_NORTHEAST_3, AP_NORTHEAST_2, AP_NORTHEAST_1, CA_CENTRAL_1, SA_EAST_1, CN_NORTH_1, US_GOV_WEST_1, + AP_SOUTHEAST_1, AP_SOUTHEAST_2, EU_CENTRAL_1, US_EAST_1, US_EAST_2, US_WEST_1, CN_NORTHWEST_1, US_WEST_2, AWS_GLOBAL, + AWS_CN_GLOBAL, AWS_US_GOV_GLOBAL, AWS_ISO_GLOBAL, AWS_ISO_B_GLOBAL)); private final boolean isGlobalRegion; diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/s3-service-metadata.java b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/s3-service-metadata.java index bae9ee6da3e3..027d9a3bc765 100644 --- a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/s3-service-metadata.java +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/s3-service-metadata.java @@ -9,6 +9,8 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServicePartitionMetadata; +import software.amazon.awssdk.regions.internal.DefaultServicePartitionMetadata; import software.amazon.awssdk.utils.ImmutableMap; @Generated("software.amazon.awssdk:codegen") @@ -19,22 +21,27 @@ public final class S3ServiceMetadata implements ServiceMetadata { private static final Map PARTITION_OVERRIDDEN_ENDPOINTS = ImmutableMap. builder().build(); private static final Map REGION_OVERRIDDEN_ENDPOINTS = ImmutableMap. builder() - .put("ap-northeast-1", "s3.ap-northeast-1.amazonaws.com").put("ap-southeast-1", "s3.ap-southeast-1.amazonaws.com") - .put("ap-southeast-2", "s3.ap-southeast-2.amazonaws.com").put("eu-west-1", "s3.eu-west-1.amazonaws.com") - .put("sa-east-1", "s3.sa-east-1.amazonaws.com").put("us-east-1", "s3.amazonaws.com") - .put("us-west-1", "s3.us-west-1.amazonaws.com").put("us-west-2", "s3.us-west-2.amazonaws.com") - .put("fips-us-gov-west-1", "s3-fips-us-gov-west-1.amazonaws.com") - .put("us-gov-west-1", "s3.us-gov-west-1.amazonaws.com").build(); - - private static final List REGIONS = Collections.unmodifiableList(Arrays.asList(Region.of("ap-northeast-1"), - Region.of("ap-northeast-2"), Region.of("ap-northeast-3"), Region.of("ap-south-1"), Region.of("ap-southeast-1"), - Region.of("ap-southeast-2"), Region.of("ca-central-1"), Region.of("eu-central-1"), Region.of("eu-west-1"), - Region.of("eu-west-2"), Region.of("eu-west-3"), Region.of("sa-east-1"), Region.of("us-east-1"), - Region.of("us-east-2"), Region.of("us-west-1"), Region.of("us-west-2"), Region.of("cn-north-1"), - Region.of("cn-northwest-1"), Region.of("fips-us-gov-west-1"), Region.of("us-gov-west-1"))); + .put("ap-northeast-1", "s3.ap-northeast-1.amazonaws.com").put("ap-southeast-1", "s3.ap-southeast-1.amazonaws.com") + .put("ap-southeast-2", "s3.ap-southeast-2.amazonaws.com").put("eu-west-1", "s3.eu-west-1.amazonaws.com") + .put("sa-east-1", "s3.sa-east-1.amazonaws.com").put("us-east-1", "s3.amazonaws.com") + .put("us-west-1", "s3.us-west-1.amazonaws.com").put("us-west-2", "s3.us-west-2.amazonaws.com") + .put("fips-us-gov-west-1", "s3-fips-us-gov-west-1.amazonaws.com") + .put("us-gov-west-1", "s3.us-gov-west-1.amazonaws.com").build(); + + private static final List REGIONS = Collections.unmodifiableList( + Arrays.asList(Region.of("ap-northeast-1"), + Region.of("ap-northeast-2"), Region.of("ap-northeast-3"), Region.of("ap-south-1"), Region.of("ap-southeast-1"), + Region.of("ap-southeast-2"), Region.of("ca-central-1"), Region.of("eu-central-1"), Region.of("eu-west-1"), + Region.of("eu-west-2"), Region.of("eu-west-3"), Region.of("sa-east-1"), Region.of("us-east-1"), + Region.of("us-east-2"), Region.of("us-west-1"), Region.of("us-west-2"), Region.of("cn-north-1"), + Region.of("cn-northwest-1"), Region.of("fips-us-gov-west-1"), Region.of("us-gov-west-1"))); private static final Map SIGNING_REGION_OVERRIDES = ImmutableMap. builder() - .put("fips-us-gov-west-1", "us-gov-west-1").build(); + .put("fips-us-gov-west-1", "us-gov-west-1").build(); + + private static final List PARTITIONS = Collections.unmodifiableList(Arrays.asList( + new DefaultServicePartitionMetadata("aws", null), new DefaultServicePartitionMetadata("aws-cn", null), + new DefaultServicePartitionMetadata("aws-us-gov", null))); @Override public List regions() { @@ -44,11 +51,16 @@ public List regions() { @Override public URI endpointFor(Region region) { return URI.create(REGION_OVERRIDDEN_ENDPOINTS.containsKey(region.id()) ? REGION_OVERRIDDEN_ENDPOINTS.get(region.id()) - : computeEndpoint(ENDPOINT_PREFIX, PARTITION_OVERRIDDEN_ENDPOINTS, region)); + : computeEndpoint(ENDPOINT_PREFIX, PARTITION_OVERRIDDEN_ENDPOINTS, region)); } @Override public Region signingRegion(Region region) { return Region.of(SIGNING_REGION_OVERRIDES.getOrDefault(region.id(), region.id())); } + + @Override + public List servicePartitions() { + return PARTITIONS; + } } diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java index b781aa2503c5..ee84f112bdba 100644 --- a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/service-metadata-provider.java @@ -1,5 +1,4 @@ package software.amazon.awssdk.regions; - import java.util.Map; import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkPublicApi; @@ -56,6 +55,7 @@ import software.amazon.awssdk.regions.servicemetadata.ElasticmapreduceServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.ElastictranscoderServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.EmailServiceMetadata; +import software.amazon.awssdk.regions.servicemetadata.EnhancedS3ServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.EntitlementMarketplaceServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.EsServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.EventsServiceMetadata; @@ -107,7 +107,6 @@ import software.amazon.awssdk.regions.servicemetadata.Route53domainsServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.RuntimeLexServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.RuntimeSagemakerServiceMetadata; -import software.amazon.awssdk.regions.servicemetadata.S3ServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.SagemakerServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.SdbServiceMetadata; import software.amazon.awssdk.regions.servicemetadata.SecretsmanagerServiceMetadata; @@ -196,7 +195,7 @@ public final class GeneratedServiceMetadataProvider implements ServiceMetadataPr .put("redshift", new RedshiftServiceMetadata()).put("rekognition", new RekognitionServiceMetadata()) .put("resource-groups", new ResourceGroupsServiceMetadata()).put("route53", new Route53ServiceMetadata()) .put("route53domains", new Route53domainsServiceMetadata()).put("runtime.lex", new RuntimeLexServiceMetadata()) - .put("runtime.sagemaker", new RuntimeSagemakerServiceMetadata()).put("s3", new S3ServiceMetadata()) + .put("runtime.sagemaker", new RuntimeSagemakerServiceMetadata()).put("s3", new EnhancedS3ServiceMetadata()) .put("sagemaker", new SagemakerServiceMetadata()).put("sdb", new SdbServiceMetadata()) .put("secretsmanager", new SecretsmanagerServiceMetadata()) .put("serverlessrepo", new ServerlessrepoServiceMetadata()) diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/sts-service-metadata.java b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/sts-service-metadata.java index b622f4a7ee45..a88b40b8378a 100644 --- a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/sts-service-metadata.java +++ b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/sts-service-metadata.java @@ -1,18 +1,3 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.regions.servicemetadata; import java.net.URI; @@ -24,6 +9,8 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServicePartitionMetadata; +import software.amazon.awssdk.regions.internal.DefaultServicePartitionMetadata; import software.amazon.awssdk.utils.ImmutableMap; @Generated("software.amazon.awssdk:codegen") @@ -51,6 +38,10 @@ public final class StsServiceMetadata implements ServiceMetadata { .put("ap-northeast-2", "ap-northeast-2").put("us-east-1-fips", "us-east-1").put("us-east-2-fips", "us-east-2") .put("us-west-1-fips", "us-west-1").put("us-west-2-fips", "us-west-2").build(); + private static final List PARTITIONS = Collections.unmodifiableList(Arrays.asList( + new DefaultServicePartitionMetadata("aws", null), new DefaultServicePartitionMetadata("aws-cn", null), + new DefaultServicePartitionMetadata("aws-us-gov", null))); + @Override public List regions() { return REGIONS; @@ -66,4 +57,9 @@ public URI endpointFor(Region region) { public Region signingRegion(Region region) { return Region.of(SIGNING_REGION_OVERRIDES.getOrDefault(region.id(), region.id())); } + + @Override + public List servicePartitions() { + return PARTITIONS; + } } diff --git a/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/endpoints.json b/codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/test-endpoints.json similarity index 100% rename from codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/endpoints.json rename to codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/test-endpoints.json diff --git a/codegen-maven-plugin/build.properties b/codegen-maven-plugin/build.properties index ecf2dae6fcb1..59c37a425758 100644 --- a/codegen-maven-plugin/build.properties +++ b/codegen-maven-plugin/build.properties @@ -1,12 +1,12 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. # A copy of the License is located at -# +# # http://aws.amazon.com/apache2.0 -# +# # or in the "license" file accompanying this file. This file 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 diff --git a/codegen-maven-plugin/pom.xml b/codegen-maven-plugin/pom.xml index bb6e839dcf38..0fad221360f8 100644 --- a/codegen-maven-plugin/pom.xml +++ b/codegen-maven-plugin/pom.xml @@ -1,6 +1,6 @@ + core software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/Generated.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/Generated.java index 82102c3d0924..3c649551a73e 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/Generated.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/Generated.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/Immutable.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/Immutable.java index 0d98cd4e1b6a..cf172c06a6d8 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/Immutable.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/Immutable.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotThreadSafe.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotThreadSafe.java index e8097e5683bb..0b43a2f2392a 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotThreadSafe.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/NotThreadSafe.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/ReviewBeforeRelease.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/ReviewBeforeRelease.java index 0cb16ed58688..8c14f26a8fea 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/ReviewBeforeRelease.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/ReviewBeforeRelease.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkInternalApi.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkInternalApi.java index da18811b4ba8..16117b3bbd1b 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkInternalApi.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkInternalApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkProtectedApi.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkProtectedApi.java index 2f47fff57bdb..edf0580666fe 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkProtectedApi.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkProtectedApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkPublicApi.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkPublicApi.java index 560369ed6652..9a8104527f7a 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkPublicApi.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkPublicApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkTestInternalApi.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkTestInternalApi.java index de45d7345a08..7cc8d4dea5b7 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkTestInternalApi.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/SdkTestInternalApi.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/ThreadSafe.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/ThreadSafe.java index 972f226428df..0df611a9c101 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/ThreadSafe.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/ThreadSafe.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/annotations/src/main/java/software/amazon/awssdk/annotations/package-info.java b/core/annotations/src/main/java/software/amazon/awssdk/annotations/package-info.java index 18e533682f61..06933e2a9790 100644 --- a/core/annotations/src/main/java/software/amazon/awssdk/annotations/package-info.java +++ b/core/annotations/src/main/java/software/amazon/awssdk/annotations/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/arns/pom.xml b/core/arns/pom.xml index 6c6fd0dcc55a..85bececede5c 100644 --- a/core/arns/pom.xml +++ b/core/arns/pom.xml @@ -1,6 +1,6 @@ + @@ -7,7 +22,7 @@ software.amazon.awssdk core - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT auth diff --git a/core/auth/src/it/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderIntegrationTest.java b/core/auth/src/it/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderIntegrationTest.java index 189c3e514043..3647d0de88a3 100644 --- a/core/auth/src/it/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderIntegrationTest.java +++ b/core/auth/src/it/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AnonymousCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AnonymousCredentialsProvider.java index 29c1662dd998..b4d7d22f4580 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AnonymousCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AnonymousCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsBasicCredentials.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsBasicCredentials.java index 14f8de09c3ad..794526712339 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsBasicCredentials.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsBasicCredentials.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentials.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentials.java index ecd24408750c..b907d77a892a 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentials.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentials.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.java index 095f42741cff..0e8ddb4aad3b 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChain.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChain.java index 12b58a726ca4..fcc62e51af42 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChain.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChain.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -160,7 +160,8 @@ private static final class BuilderImpl implements Builder { private Boolean reuseLastProviderEnabled = true; private List credentialsProviders = new ArrayList<>(); - private BuilderImpl() {} + private BuilderImpl() { + } @Override public Builder reuseLastProviderEnabled(Boolean reuseLastProviderEnabled) { diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java index 94fca729c651..8acd9efd02b9 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java index 2229ef8dc56e..620e32decfe2 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ChildProfileCredentialsProviderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java index 1872e10cc133..1d2bdc249ab8 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ import static java.util.Collections.singletonMap; import static java.util.Collections.unmodifiableSet; -import static java.util.stream.Collectors.joining; import java.io.IOException; import java.net.URI; @@ -138,7 +137,7 @@ private URI createGenericContainerUrl() { uri, SdkSystemSetting.AWS_CONTAINER_CREDENTIALS_FULL_URI .environmentVariable(), - ALLOWED_HOSTS.stream().collect(joining(",")))) + String.join(",", ALLOWED_HOSTS))) .build(); } return uri; diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/CredentialUtils.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/CredentialUtils.java index 09bcfcddd5fe..6e4879b59c2a 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/CredentialUtils.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/CredentialUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.java index a8cbf236fbbb..764112223ae4 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.auth.credentials.internal.LazyAwsCredentialsProvider; +import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.utils.SdkAutoCloseable; import software.amazon.awssdk.utils.ToString; @@ -34,6 +35,7 @@ * @see SystemPropertyCredentialsProvider * @see EnvironmentVariableCredentialsProvider * @see ProfileCredentialsProvider + * @see WebIdentityTokenFileCredentialsProvider * @see ContainerCredentialsProvider * @see InstanceProfileCredentialsProvider */ @@ -70,7 +72,11 @@ private static LazyAwsCredentialsProvider createChain(Builder builder) { AwsCredentialsProvider[] credentialsProviders = new AwsCredentialsProvider[] { SystemPropertyCredentialsProvider.create(), EnvironmentVariableCredentialsProvider.create(), - ProfileCredentialsProvider.create(), + WebIdentityTokenFileCredentialsProvider.create(), + ProfileCredentialsProvider.builder() + .profileFile(builder.profileFile) + .profileName(builder.profileName) + .build(), ContainerCredentialsProvider.builder() .asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled) .build(), @@ -114,13 +120,26 @@ public String toString() { * Configuration that defines the {@link DefaultCredentialsProvider}'s behavior. */ public static final class Builder { + private ProfileFile profileFile; + private String profileName; private Boolean reuseLastProviderEnabled = true; private Boolean asyncCredentialUpdateEnabled = false; /** * Created with {@link #builder()}. */ - private Builder() {} + private Builder() { + } + + public Builder profileFile(ProfileFile profileFile) { + this.profileFile = profileFile; + return this; + } + + public Builder profileName(String profileName) { + this.profileName = profileName; + return this; + } /** * Controls whether the provider should reuse the last successful credentials provider in the chain. Reusing the last diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java index 3efbe3367ea1..18d566687718 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/EnvironmentVariableCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java index 6bad1959657f..f2e1aa03d600 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java index 7ddb2c6adf7c..3a704dbcb969 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,9 +17,13 @@ import java.io.IOException; import java.net.URI; +import java.util.HashMap; +import java.util.Map; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.internal.util.UserAgentUtils; +import software.amazon.awssdk.regions.internal.util.EC2MetadataUtils; import software.amazon.awssdk.regions.util.HttpResourcesUtils; import software.amazon.awssdk.regions.util.ResourcesEndpointProvider; import software.amazon.awssdk.utils.ToString; @@ -33,9 +37,9 @@ */ @SdkPublicApi public final class InstanceProfileCredentialsProvider extends HttpCredentialsProvider { + private static final String EC2_METADATA_TOKEN_HEADER = "x-aws-ec2-metadata-token"; private static final String SECURITY_CREDENTIALS_RESOURCE = "/latest/meta-data/iam/security-credentials/"; - private final ResourcesEndpointProvider credentialsEndpointProvider = new InstanceProviderCredentialsEndpointProvider(); /** * @see #builder() @@ -62,7 +66,7 @@ public static InstanceProfileCredentialsProvider create() { @Override protected ResourcesEndpointProvider getCredentialsEndpointProvider() { - return credentialsEndpointProvider; + return new InstanceProviderCredentialsEndpointProvider(getToken()); } @Override @@ -75,13 +79,45 @@ public String toString() { return ToString.create("InstanceProfileCredentialsProvider"); } + private String getToken() { + return EC2MetadataUtils.getToken(); + } + + private static ResourcesEndpointProvider includeTokenHeader(ResourcesEndpointProvider provider, String token) { + return new ResourcesEndpointProvider() { + @Override + public URI endpoint() throws IOException { + return provider.endpoint(); + } + + @Override + public Map headers() { + Map headers = new HashMap<>(provider.headers()); + headers.put(EC2_METADATA_TOKEN_HEADER, token); + return headers; + } + }; + } + private static final class InstanceProviderCredentialsEndpointProvider implements ResourcesEndpointProvider { + private final String metadataToken; + + private InstanceProviderCredentialsEndpointProvider(String metadataToken) { + this.metadataToken = metadataToken; + } + @Override public URI endpoint() throws IOException { String host = SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.getStringValueOrThrow(); URI endpoint = URI.create(host + SECURITY_CREDENTIALS_RESOURCE); - String securityCredentialsList = HttpResourcesUtils.instance().readResource(endpoint); + ResourcesEndpointProvider endpointProvider = () -> endpoint; + + if (metadataToken != null) { + endpointProvider = includeTokenHeader(endpointProvider, metadataToken); + } + + String securityCredentialsList = HttpResourcesUtils.instance().readResource(endpointProvider); String[] securityCredentials = securityCredentialsList.trim().split("\n"); if (securityCredentials.length == 0) { @@ -90,8 +126,23 @@ public URI endpoint() throws IOException { return URI.create(host + SECURITY_CREDENTIALS_RESOURCE + securityCredentials[0]); } + + @Override + public Map headers() { + Map requestHeaders = new HashMap<>(); + requestHeaders.put("User-Agent", UserAgentUtils.getUserAgent()); + requestHeaders.put("Accept", "*/*"); + requestHeaders.put("Connection", "keep-alive"); + + if (metadataToken != null) { + requestHeaders.put(EC2_METADATA_TOKEN_HEADER, metadataToken); + } + + return requestHeaders; + } } + /** * A builder for creating a custom a {@link InstanceProfileCredentialsProvider}. */ diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java index 2f1cee14bd55..fac87fbb0e69 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ * start to be refreshed. This allows the credentials to be refreshed *before* they are reported to expire. Default: 15 * seconds. *
  • ProcessOutputLimit - The maximum amount of data that can be returned by the external process before an exception is - * raised. Default: 1024 bytes.
  • + * raised. Default: 64000 bytes (64KB). * */ @SdkPublicApi @@ -222,12 +222,13 @@ public static class Builder { private Boolean asyncCredentialUpdateEnabled = false; private String command; private Duration credentialRefreshThreshold = Duration.ofSeconds(15); - private long processOutputLimit = 1024; + private long processOutputLimit = 64000; /** * @see #builder() */ - private Builder() {} + private Builder() { + } /** * Configure whether the provider should fetch credentials asynchronously in the background. If this is true, threads are @@ -264,7 +265,7 @@ public Builder credentialRefreshThreshold(Duration credentialRefreshThreshold) { * Configure the maximum amount of data that can be returned by the external process before an exception is * raised. * - *

    Default: 1024 bytes.

    + *

    Default: 64000 bytes (64KB).

    */ public Builder processOutputLimit(long outputByteLimit) { this.processOutputLimit = outputByteLimit; diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProvider.java index fe13174f2c31..1a4dd42e870d 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -21,7 +21,6 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; import software.amazon.awssdk.auth.credentials.internal.ProfileCredentialsUtils; -import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.profiles.ProfileFileSystemSetting; @@ -163,7 +162,7 @@ public interface Builder { /** * Define the name of the profile that should be used by this credentials provider. By default, the value in - * {@link SdkSystemSetting#AWS_PROFILE} is used. + * {@link ProfileFileSystemSetting#AWS_PROFILE} is used. */ Builder profileName(String profileName); diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProvider.java index e52efdab2e83..aa67c4dd68b4 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java index b4320ffb6580..135b6ce88565 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/SystemPropertyCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenCredentialsProviderFactory.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenCredentialsProviderFactory.java index 9faeb71c373e..0772b895f0a9 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenCredentialsProviderFactory.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenCredentialsProviderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java index ab439ada4998..168ecb3a1965 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/WebIdentityTokenFileCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -156,7 +156,7 @@ public Builder webIdentityTokenFile(Path webIdentityTokenFile) { return this; } - public void setwebIdentityTokenFile(Path webIdentityTokenFile) { + public void setWebIdentityTokenFile(Path webIdentityTokenFile) { webIdentityTokenFile(webIdentityTokenFile); } diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ContainerCredentialsRetryPolicy.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ContainerCredentialsRetryPolicy.java index 352dd2aff1ce..448d19aee53c 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ContainerCredentialsRetryPolicy.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ContainerCredentialsRetryPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/CredentialSourceType.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/CredentialSourceType.java index 61c82a75bcc9..db3de2845d49 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/CredentialSourceType.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/CredentialSourceType.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProvider.java index 54260eca2e8a..6999e25af250 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.utils.IoUtils; +import software.amazon.awssdk.utils.Lazy; import software.amazon.awssdk.utils.SdkAutoCloseable; import software.amazon.awssdk.utils.ToString; @@ -29,11 +30,10 @@ */ @SdkInternalApi public class LazyAwsCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable { - private final Supplier delegateConstructor; - private volatile AwsCredentialsProvider delegate; + private final Lazy delegate; private LazyAwsCredentialsProvider(Supplier delegateConstructor) { - this.delegateConstructor = delegateConstructor; + this.delegate = new Lazy<>(delegateConstructor); } public static LazyAwsCredentialsProvider create(Supplier delegateConstructor) { @@ -42,14 +42,7 @@ public static LazyAwsCredentialsProvider create(Supplier @Override public AwsCredentials resolveCredentials() { - if (delegate == null) { - synchronized (this) { - if (delegate == null) { - delegate = delegateConstructor.get(); - } - } - } - return delegate.resolveCredentials(); + return delegate.getValue().resolveCredentials(); } @Override @@ -60,7 +53,6 @@ public void close() { @Override public String toString() { return ToString.builder("LazyAwsCredentialsProvider") - .add("delegateConstructor", delegateConstructor) .add("delegate", delegate) .build(); } diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java index d2646ec6a57a..c57cfe17b6c9 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/SystemSettingsCredentialsProvider.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/SystemSettingsCredentialsProvider.java index 74e84a210900..c84cc44c6237 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/SystemSettingsCredentialsProvider.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/SystemSettingsCredentialsProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityCredentialsUtils.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityCredentialsUtils.java index 9c2136ec4db0..b73f7ec1dfce 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityCredentialsUtils.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityCredentialsUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -28,7 +28,8 @@ public final class WebIdentityCredentialsUtils { private static final String STS_WEB_IDENTITY_CREDENTIALS_PROVIDER_FACTORY = "software.amazon.awssdk.services.sts.internal.StsWebIdentityCredentialsProviderFactory"; - private WebIdentityCredentialsUtils() {} + private WebIdentityCredentialsUtils() { + } /** * Resolves the StsWebIdentityCredentialsProviderFactory from the Sts module if on the classpath to allow diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java index 06375802e356..0d6687edd5ac 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityTokenCredentialProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java index 05934f34b9f3..bad5428c3a8c 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java index ffc9a3ec78e6..271f7a95457c 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/Aws4UnsignedPayloadSigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java index 9d27859a473a..6dc3385d0ef5 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsS3V4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java index 5f20a9c92957..5b8fcfc026d0 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/AwsSignerExecutionAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java index 560057f54498..474046cf2202 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/EventStreamAws4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,7 +20,8 @@ @SdkProtectedApi public final class EventStreamAws4Signer extends BaseEventStreamAsyncAws4Signer { - private EventStreamAws4Signer() {} + private EventStreamAws4Signer() { + } public static EventStreamAws4Signer create() { return new EventStreamAws4Signer(); diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/S3SignerExecutionAttribute.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/S3SignerExecutionAttribute.java index 391a58c9853c..69576d3d193f 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/S3SignerExecutionAttribute.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/S3SignerExecutionAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAws4Signer.java index 3d841b58eea6..0ae639f517dd 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAws4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAws4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,16 +15,15 @@ package software.amazon.awssdk.auth.signer.internal; -import static software.amazon.awssdk.utils.DateUtils.numberOfDaysSinceEpoch; import static software.amazon.awssdk.utils.StringUtils.lowerCase; import java.io.InputStream; import java.nio.charset.Charset; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.TimeUnit; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; @@ -160,24 +159,28 @@ protected abstract void processRequestPayload(SdkHttpFullRequest.Builder mutable * http://docs.aws.amazon * .com/general/latest/gr/sigv4-calculate-signature.html */ - protected byte[] deriveSigningKey(AwsCredentials credentials, Aws4SignerRequestParams signerRequestParams) { - - String cacheKey = computeSigningCacheKeyName(credentials, signerRequestParams); - long daysSinceEpochSigningDate = numberOfDaysSinceEpoch(signerRequestParams.getRequestSigningDateTimeMilli()); + protected final byte[] deriveSigningKey(AwsCredentials credentials, Aws4SignerRequestParams signerRequestParams) { + return deriveSigningKey(credentials, + Instant.ofEpochMilli(signerRequestParams.getRequestSigningDateTimeMilli()), + signerRequestParams.getRegionName(), + signerRequestParams.getServiceSigningName()); + } + protected final byte[] deriveSigningKey(AwsCredentials credentials, Instant signingInstant, String region, String service) { + String cacheKey = createSigningCacheKeyName(credentials, region, service); SignerKey signerKey = SIGNER_CACHE.get(cacheKey); - if (signerKey != null && daysSinceEpochSigningDate == signerKey.getNumberOfDaysSinceEpoch()) { + if (signerKey != null && signerKey.isValidForDate(signingInstant)) { return signerKey.getSigningKey(); } LOG.trace(() -> "Generating a new signing key as the signing key not available in the cache for the date: " + - TimeUnit.DAYS.toMillis(daysSinceEpochSigningDate)); + signingInstant.toEpochMilli()); byte[] signingKey = newSigningKey(credentials, - signerRequestParams.getFormattedRequestSigningDate(), - signerRequestParams.getRegionName(), - signerRequestParams.getServiceSigningName()); - SIGNER_CACHE.add(cacheKey, new SignerKey(daysSinceEpochSigningDate, signingKey)); + Aws4SignerUtils.formatDateStamp(signingInstant), + region, + service); + SIGNER_CACHE.add(cacheKey, new SignerKey(signingInstant, signingKey)); return signingKey; } @@ -228,14 +231,10 @@ private String createStringToSign(String canonicalRequest, return stringToSign; } - - /** - * Computes the name to be used to reference the signing key in the cache. - */ - private String computeSigningCacheKeyName(AwsCredentials credentials, - Aws4SignerRequestParams signerRequestParams) { - return credentials.secretAccessKey() + "-" + signerRequestParams.getRegionName() + "-" + - signerRequestParams.getServiceSigningName(); + private String createSigningCacheKeyName(AwsCredentials credentials, + String regionName, + String serviceName) { + return credentials.secretAccessKey() + "-" + regionName + "-" + serviceName; } /** diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAwsSigner.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAwsSigner.java index 893705186fb2..aa7e31376207 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAwsSigner.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AbstractAwsSigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AsyncSigV4SubscriberAdapter.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AsyncSigV4SubscriberAdapter.java index 52351111d2eb..9cfa40f7b094 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AsyncSigV4SubscriberAdapter.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AsyncSigV4SubscriberAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParams.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParams.java index eb4ae5ae59cc..8c577e81cde5 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParams.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerUtils.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerUtils.java index 7b543198a5e3..2ca42ca7d3e5 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerUtils.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -46,6 +46,10 @@ public static String formatDateStamp(long timeMilli) { return DATE_FORMATTER.format(Instant.ofEpochMilli(timeMilli)); } + public static String formatDateStamp(Instant instant) { + return DATE_FORMATTER.format(instant); + } + /** * Returns a string representation of the given date time in * yyyyMMdd'T'HHmmss'Z' format. The date returned is in the UTC zone. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AwsChunkedEncodingInputStream.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AwsChunkedEncodingInputStream.java index 8abfd4e2aa89..d11c1b14bcb8 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AwsChunkedEncodingInputStream.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/AwsChunkedEncodingInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAsyncAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAsyncAws4Signer.java index 3ab593570bf5..df25b40c41ee 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAsyncAws4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAsyncAws4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import java.util.regex.Pattern; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; -import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.signer.params.Aws4SignerParams; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.exception.SdkClientException; @@ -57,11 +56,8 @@ public AsyncRequestBody signAsyncRequestBody(SdkHttpFullRequest request, AsyncRe @SdkTestInternalApi protected final AsyncRequestBody signAsync(SdkHttpFullRequest request, AsyncRequestBody asyncRequestBody, Aws4SignerRequestParams requestParams, Aws4SignerParams signingParams) { - AwsCredentials sanitizedCredentials = sanitizeCredentials(signingParams.awsCredentials()); - byte[] signingKey = deriveSigningKey(sanitizedCredentials, requestParams); - String headerSignature = getHeaderSignature(request); - return transformRequestProvider(headerSignature, signingKey, requestParams, signingParams, asyncRequestBody); + return transformRequestProvider(headerSignature, requestParams, signingParams, asyncRequestBody); } /** @@ -70,7 +66,6 @@ protected final AsyncRequestBody signAsync(SdkHttpFullRequest request, AsyncRequ * Can be overriden by subclasses to provide specific signing method */ protected abstract AsyncRequestBody transformRequestProvider(String headerSignature, - byte[] signingKey, Aws4SignerRequestParams signerRequestParams, Aws4SignerParams signerParams, AsyncRequestBody asyncRequestBody); diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java index 1ac07719f21b..256b143ea845 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseAws4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4Signer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4Signer.java index d7b3cf179f3f..e50ac4e1450f 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4Signer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4Signer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,7 +20,9 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Optional; import java.util.TreeMap; @@ -28,6 +30,7 @@ import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.signer.params.Aws4SignerParams; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.async.SdkPublisher; @@ -49,6 +52,8 @@ public abstract class BaseEventStreamAsyncAws4Signer extends BaseAsyncAws4Signer private static final String HTTP_CONTENT_SHA_256 = "STREAMING-AWS4-HMAC-SHA256-EVENTS"; private static final String EVENT_STREAM_PAYLOAD = "AWS4-HMAC-SHA256-PAYLOAD"; + private static final int PAYLOAD_TRUNCATE_LENGTH = 32; + protected BaseEventStreamAsyncAws4Signer() { } @@ -67,7 +72,6 @@ public SdkHttpFullRequest sign(SdkHttpFullRequest request, Aws4SignerParams sign @Override protected AsyncRequestBody transformRequestProvider(String headerSignature, - byte[] signingKey, Aws4SignerRequestParams signerRequestParams, Aws4SignerParams signerParams, AsyncRequestBody asyncRequestBody) { @@ -80,7 +84,8 @@ protected AsyncRequestBody transformRequestProvider(String headerSignature, * Map publisher with signing function */ Publisher publisherWithSignedFrame = - transformRequestBodyPublisher(publisherWithTrailingEmptyFrame, headerSignature, signingKey, signerRequestParams); + transformRequestBodyPublisher(publisherWithTrailingEmptyFrame, headerSignature, + signerParams.awsCredentials(), signerRequestParams); AsyncRequestBody transformedRequestBody = AsyncRequestBody.fromPublisher(publisherWithSignedFrame); @@ -105,16 +110,16 @@ private static Publisher appendEmptyFrame(Publisher publ } private Publisher transformRequestBodyPublisher(Publisher publisher, String headerSignature, - byte[] signingKey, Aws4SignerRequestParams signerRequestParams) { + AwsCredentials credentials, + Aws4SignerRequestParams signerRequestParams) { return SdkPublisher.adapt(publisher) - .map(getDataFrameSigner(headerSignature, signingKey, signerRequestParams)); + .map(getDataFrameSigner(headerSignature, credentials, signerRequestParams)); } - private Function getDataFrameSigner(String headerSignature, byte[] signingKey, + private Function getDataFrameSigner(String headerSignature, + AwsCredentials credentials, Aws4SignerRequestParams signerRequestParams) { return new Function() { - - final byte[] key = signingKey; final Aws4SignerRequestParams requestParams = signerRequestParams; /** @@ -129,14 +134,21 @@ public ByteBuffer apply(ByteBuffer byteBuffer) { */ Map nonSignatureHeaders = new HashMap<>(); Instant signingInstant = requestParams.getSigningClock().instant(); - String signingDate = Aws4SignerUtils.formatTimestamp(signingInstant); nonSignatureHeaders.put(EVENT_STREAM_DATE, HeaderValue.fromTimestamp(signingInstant)); + /** + * Derive Signing Key + */ + AwsCredentials sanitizedCredentials = sanitizeCredentials(credentials); + byte[] signingKey = deriveSigningKey(sanitizedCredentials, + signingInstant, + requestParams.getRegionName(), + requestParams.getServiceSigningName()); /** * Calculate rolling signature */ byte[] payload = byteBuffer.array(); - byte[] signatureBytes = signEventStream(priorSignature, key, signingDate, requestParams, + byte[] signatureBytes = signEventStream(priorSignature, signingKey, signingInstant, requestParams, nonSignatureHeaders, payload); priorSignature = BinaryUtils.toHex(signatureBytes); @@ -151,6 +163,13 @@ public ByteBuffer apply(ByteBuffer byteBuffer) { * Encode signed event to byte */ Message signedMessage = new Message(sortHeaders(headers), payload); + + if (LOG.isLoggingLevelEnabled("trace")) { + LOG.trace(() -> "Signed message: " + toDebugString(signedMessage, false)); + } else { + LOG.debug(() -> "Signed message: " + toDebugString(signedMessage, true)); + } + return signedMessage.toByteBuffer(); } }; @@ -162,7 +181,7 @@ public ByteBuffer apply(ByteBuffer byteBuffer) { * * @param priorSignature signature of previous frame (Header frame is the 0th frame) * @param signingKey derived signing key - * @param date siging date + * @param signingInstant the instant at which this message is being signed * @param requestParams request parameters * @param nonSignatureHeaders non-signature headers * @param payload event stream payload @@ -171,7 +190,7 @@ public ByteBuffer apply(ByteBuffer byteBuffer) { private byte[] signEventStream( String priorSignature, byte[] signingKey, - String date, + Instant signingInstant, Aws4SignerRequestParams requestParams, Map nonSignatureHeaders, byte[] payload) { @@ -180,9 +199,9 @@ private byte[] signEventStream( String stringToSign = EVENT_STREAM_PAYLOAD + SignerConstant.LINE_SEPARATOR + - date + + Aws4SignerUtils.formatTimestamp(signingInstant) + SignerConstant.LINE_SEPARATOR + - requestParams.getScope() + + computeScope(signingInstant, requestParams) + SignerConstant.LINE_SEPARATOR + priorSignature + SignerConstant.LINE_SEPARATOR + @@ -195,6 +214,13 @@ private byte[] signEventStream( SigningAlgorithm.HmacSHA256); } + private String computeScope(Instant signingInstant, Aws4SignerRequestParams requestParams) { + return Aws4SignerUtils.formatDateStamp(signingInstant) + "/" + + requestParams.getRegionName() + "/" + + requestParams.getServiceSigningName() + "/" + + SignerConstant.AWS4_TERMINATOR; + } + /** * Sort headers in alphabetic order, with exception that EVENT_STREAM_SIGNATURE header always at last * @@ -244,4 +270,45 @@ public Optional contentLength() { } } + static String toDebugString(Message m, boolean truncatePayload) { + StringBuilder sb = new StringBuilder("Message = {headers={"); + Map headers = m.getHeaders(); + + Iterator> headersIter = headers.entrySet().iterator(); + + while (headersIter.hasNext()) { + Map.Entry h = headersIter.next(); + + sb.append(h.getKey()).append("={").append(h.getValue().toString()).append("}"); + + if (headersIter.hasNext()) { + sb.append(", "); + } + } + + sb.append("}, payload="); + + byte[] payload = m.getPayload(); + byte[] payloadToLog; + + // We don't actually need to truncate if the payload length is already within the truncate limit + truncatePayload = truncatePayload && payload.length > PAYLOAD_TRUNCATE_LENGTH; + + if (truncatePayload) { + // Would be nice if BinaryUtils.toHex() could take an array index range instead so we don't need to copy + payloadToLog = Arrays.copyOf(payload, PAYLOAD_TRUNCATE_LENGTH); + } else { + payloadToLog = payload; + } + + sb.append(BinaryUtils.toHex(payloadToLog)); + + if (truncatePayload) { + sb.append("..."); + } + + sb.append("}"); + + return sb.toString(); + } } diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BoundedLinkedHashMap.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BoundedLinkedHashMap.java index 59c4aa01f94d..20d2d75bd070 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BoundedLinkedHashMap.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/BoundedLinkedHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/ChunkContentIterator.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/ChunkContentIterator.java index 527b826d0c33..0a40d7571812 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/ChunkContentIterator.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/ChunkContentIterator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/DecodedStreamBuffer.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/DecodedStreamBuffer.java index 5161ce43ba21..9cb5b48283a8 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/DecodedStreamBuffer.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/DecodedStreamBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/FifoCache.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/FifoCache.java index 29838e0c4f0c..ccb3b108915d 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/FifoCache.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/FifoCache.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -43,7 +43,7 @@ public FifoCache(final int maxSize) { throw new IllegalArgumentException("maxSize " + maxSize + " must be at least 1"); } - map = new BoundedLinkedHashMap(maxSize); + map = new BoundedLinkedHashMap<>(maxSize); ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); rlock = lock.readLock(); wlock = lock.writeLock(); diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerConstant.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerConstant.java index f211641d758a..591e80746204 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerConstant.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerConstant.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerKey.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerKey.java index ed2ef8cb1002..4dc7dcde2526 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerKey.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,8 +15,10 @@ package software.amazon.awssdk.auth.signer.internal; +import java.time.Instant; import software.amazon.awssdk.annotations.Immutable; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.DateUtils; /** * Holds the signing key and the number of days since epoch for the date for @@ -26,29 +28,25 @@ @SdkInternalApi public final class SignerKey { - private final long numberOfDaysSinceEpoch; + private final long daysSinceEpoch; private final byte[] signingKey; - public SignerKey(long numberOfDaysSinceEpoch, byte[] signingKey) { - if (numberOfDaysSinceEpoch <= 0L) { + public SignerKey(Instant date, byte[] signingKey) { + if (date == null) { throw new IllegalArgumentException( - "Not able to cache signing key. Signing date to be cached is invalid"); + "Not able to cache signing key. Signing date to be is null"); } if (signingKey == null) { throw new IllegalArgumentException( "Not able to cache signing key. Signing Key to be cached are null"); } - this.numberOfDaysSinceEpoch = numberOfDaysSinceEpoch; + this.daysSinceEpoch = DateUtils.numberOfDaysSinceEpoch(date.toEpochMilli()); this.signingKey = signingKey.clone(); } - /** - * Returns the number of days since epoch for the date used for generating - * signing key. - */ - public long getNumberOfDaysSinceEpoch() { - return numberOfDaysSinceEpoch; + public boolean isValidForDate(Instant other) { + return daysSinceEpoch == DateUtils.numberOfDaysSinceEpoch(other.toEpochMilli()); } /** diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SigningAlgorithm.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SigningAlgorithm.java index ddf424ef0990..388c1b38112f 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SigningAlgorithm.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SigningAlgorithm.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4PresignerParams.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4PresignerParams.java index 80fb5c1f0baa..4a3fff21cd92 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4PresignerParams.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4PresignerParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4SignerParams.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4SignerParams.java index 40c0a7719fdd..457dc43ccc12 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4SignerParams.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/Aws4SignerParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/AwsS3V4SignerParams.java b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/AwsS3V4SignerParams.java index 40e56a10b5a6..9eeb5a5001a7 100644 --- a/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/AwsS3V4SignerParams.java +++ b/core/auth/src/main/java/software/amazon/awssdk/auth/signer/params/AwsS3V4SignerParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChainTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChainTest.java index cd33ba93b449..ea8b688e3161 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChainTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/AwsCredentialsProviderChainTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsEndpointProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsEndpointProviderTest.java index b4d163d23ad3..d7ccc4622a30 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsEndpointProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsEndpointProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java index 5657e6988207..507b855d3831 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsRetryPolicyTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsRetryPolicyTest.java index b962e4366141..38b4b7c8d4fd 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsRetryPolicyTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ContainerCredentialsRetryPolicyTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/CredentialsEndpointRetryParametersTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/CredentialsEndpointRetryParametersTest.java index 67f59266c121..aad419e649ba 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/CredentialsEndpointRetryParametersTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/CredentialsEndpointRetryParametersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/EC2MetadataServiceMock.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/EC2MetadataServiceMock.java index 99d0e7d7b140..495fff4f943d 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/EC2MetadataServiceMock.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/EC2MetadataServiceMock.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -26,6 +26,8 @@ import java.net.Socket; import java.util.List; import org.apache.commons.io.IOUtils; +import software.amazon.awssdk.auth.signer.internal.SignerTestUtils; +import software.amazon.awssdk.utils.StringUtils; /** * Mock server for imitating the Amazon EC2 Instance Metadata Service. Tests can @@ -99,6 +101,7 @@ private ServerSocket startServerSocket() { * and response with a predefined response file. */ private static class EC2MockMetadataServiceListenerThread extends Thread { + private static final String TOKEN_RESOURCE_PATH = "/latest/api/token"; private ServerSocket serverSocket; private final String credentialsResource; private String responseFileName; @@ -164,7 +167,13 @@ public void run() { .toString()); outputStream.write(httpResponse.getBytes()); - } else { + } else if (TOKEN_RESOURCE_PATH.equals(resourcePath)) { + httpResponse = "HTTP/1.1 404 Not Found\r\n" + + "Content-Length: 0\r\n" + + "\r\n"; + outputStream.write(httpResponse.getBytes()); + } + else { throw new RuntimeException("Unknown resource requested: " + resourcePath); } } catch (IOException e) { diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProviderTest.java index 15d889eca9e2..3e16b3dd3f05 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/HttpCredentialsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java new file mode 100644 index 000000000000..17b9b71a5b80 --- /dev/null +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/InstanceProfileCredentialsProviderTest.java @@ -0,0 +1,213 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.auth.credentials; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.put; +import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static org.hamcrest.Matchers.instanceOf; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import java.net.SocketTimeoutException; +import java.time.Duration; +import java.time.Instant; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.internal.util.UserAgentUtils; +import software.amazon.awssdk.utils.DateUtils; + +public class InstanceProfileCredentialsProviderTest { + private static final String TOKEN_RESOURCE_PATH = "/latest/api/token"; + private static final String CREDENTIALS_RESOURCE_PATH = "/latest/meta-data/iam/security-credentials/"; + private static final String STUB_CREDENTIALS = "{\"AccessKeyId\":\"ACCESS_KEY_ID\",\"SecretAccessKey\":\"SECRET_ACCESS_KEY\"," + + "\"Expiration\":\"" + DateUtils.formatIso8601Date(Instant.now().plus(Duration.ofDays(1))) + + "\"}"; + private static final String TOKEN_HEADER = "x-aws-ec2-metadata-token"; + private static final String EC2_METADATA_TOKEN_TTL_HEADER = "x-aws-ec2-metadata-token-ttl-seconds"; + + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public WireMockRule mockMetadataEndpoint = new WireMockRule(); + + @Before + public void methodSetup() { + System.setProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property(), "http://localhost:" + mockMetadataEndpoint.port()); + } + + @AfterClass + public static void teardown() { + System.clearProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property()); + } + + @Test + public void resolveCredentials_metadataLookupDisabled_throws() { + System.setProperty(SdkSystemSetting.AWS_EC2_METADATA_DISABLED.property(), "true"); + thrown.expect(SdkClientException.class); + thrown.expectMessage("Loading credentials from local endpoint is disabled"); + try { + InstanceProfileCredentialsProvider.builder().build().resolveCredentials(); + } finally { + System.clearProperty(SdkSystemSetting.AWS_EC2_METADATA_DISABLED.property()); + } + } + + @Test + public void resolveCredentials_requestsIncludeUserAgent() { + String stubToken = "some-token"; + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withBody(stubToken))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + String userAgentHeader = "User-Agent"; + String userAgent = UserAgentUtils.getUserAgent(); + WireMock.verify(putRequestedFor(urlPathEqualTo(TOKEN_RESOURCE_PATH)).withHeader(userAgentHeader, equalTo(userAgent))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).withHeader(userAgentHeader, equalTo(userAgent))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).withHeader(userAgentHeader, equalTo(userAgent))); + } + + @Test + public void resolveCredentials_queriesTokenResource() { + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withBody("some-token"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + WireMock.verify(putRequestedFor(urlPathEqualTo(TOKEN_RESOURCE_PATH)).withHeader(EC2_METADATA_TOKEN_TTL_HEADER, equalTo("21600"))); + } + + @Test + public void resolveCredentials_queriesTokenResource_includedInCredentialsRequests() { + String stubToken = "some-token"; + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withBody(stubToken))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).withHeader(TOKEN_HEADER, equalTo(stubToken))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).withHeader(TOKEN_HEADER, equalTo(stubToken))); + } + + @Test + public void resolveCredentials_queriesTokenResource_403Error_fallbackToInsecure() { + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withStatus(403).withBody("oops"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile"))); + } + + @Test + public void resolveCredentials_queriesTokenResource_404Error_fallbackToInsecure() { + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withStatus(404).withBody("oops"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile"))); + } + + @Test + public void resolveCredentials_queriesTokenResource_405Error_fallbackToInsecure() { + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withStatus(405).withBody("oops"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile"))); + } + + @Test + public void resolveCredentials_queriesTokenResource_400Error_throws() { + thrown.expect(SdkClientException.class); + thrown.expectMessage("token"); + + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withStatus(400).withBody("oops"))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + } + + @Test + public void resolveCredentials_queriesTokenResource_socketTimeout_fallbackToInsecure() { + stubFor(put(urlPathEqualTo(TOKEN_RESOURCE_PATH)).willReturn(aResponse().withBody("some-token").withFixedDelay(Integer.MAX_VALUE))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH)).willReturn(aResponse().withBody("some-profile"))); + stubFor(get(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile")).willReturn(aResponse().withBody(STUB_CREDENTIALS))); + + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH))); + WireMock.verify(getRequestedFor(urlPathEqualTo(CREDENTIALS_RESOURCE_PATH + "some-profile"))); + } + + @Test + public void resolveCredentials_endpointSettingEmpty_throws() { + thrown.expect(SdkClientException.class); + + System.setProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property(), ""); + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + } + + @Test + public void resolveCredentials_endpointSettingHostNotExists_throws() { + thrown.expect(SdkClientException.class); + + System.setProperty(SdkSystemSetting.AWS_EC2_METADATA_SERVICE_ENDPOINT.property(), "some-host-that-does-not-exist"); + InstanceProfileCredentialsProvider provider = InstanceProfileCredentialsProvider.builder().build(); + + provider.resolveCredentials(); + } +} diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/NoopTestRequest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/NoopTestRequest.java index 3cfbacec90d9..ba1e9eb60163 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/NoopTestRequest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/NoopTestRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java index 81fc65413d52..95dc28c091a5 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProcessCredentialsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -24,6 +24,8 @@ import java.io.UncheckedIOException; import java.time.Duration; import java.time.Instant; + +import org.assertj.core.api.Assertions; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -33,6 +35,9 @@ import software.amazon.awssdk.utils.Platform; public class ProcessCredentialsProviderTest { + + private final static String PROCESS_RESOURCE_PATH = "/resources/process/"; + private final static String RANDOM_SESSION_TOKEN = "RANDOM_TOKEN"; private static String scriptLocation; @BeforeClass @@ -98,8 +103,10 @@ public void resultsAreCached() { public void expirationBufferOverrideIsApplied() { ProcessCredentialsProvider credentialsProvider = ProcessCredentialsProvider.builder() - .command(scriptLocation + " accessKeyId secretAccessKey sessionToken " + - DateUtils.formatIso8601Date(Instant.now().plusSeconds(20))) + .command(String.format("%s accessKeyId secretAccessKey %s %s", + scriptLocation, + RANDOM_SESSION_TOKEN, + DateUtils.formatIso8601Date(Instant.now().plusSeconds(20)))) .credentialRefreshThreshold(Duration.ofSeconds(20)) .build(); @@ -132,10 +139,36 @@ public void processOutputLimitIsEnforced() { .resolveCredentials(); } + @Test + public void processOutputLimitDefaultPassesLargeInput() { + + String LONG_SESSION_TOKEN = "lYzvmByqdS1E69QQVEavDDHabQ2GuYKYABKRA4xLbAXpdnFtV030UH4" + + "bQoZWCDcfADFvBwBm3ixEFTYMjn5XQozpFV2QAsWHirCVcEJ5DC60KPCNBcDi4KLNJfbsp3r6kKTOmYOeqhEyiC4emDX33X2ppZsa5" + + "1iwr6ShIZPOUPmuR4WDglmWubgO2q5tZv48xA5idkcHEmtGdoL343sY24q4gMh21eeBnF6ikjZdfvZ0Mn86UQ8r05AD346rSwM5bFs" + + "t019ZkJIjLHD3HoKJ44EndRvSvQClXfJCmmQDH5INiXdFLLNm0dzT3ynbVIW5x1YYBWptyts4NUSy2eJ3dTPjYICpQVCkbuNVA7PqR" + + "ctUyE8lU7uvnrIVnx9xTgl34J6D9VJKHQkPuGvbtN6w4CVtXoPAQcE8tlkKyOQmIeqEahhaqLW15t692SI6hwBW0b8DxCQawX5ukt4" + + "f5gZoRFz3u8qHMSnm5oEnTgv7C5AAs0V680YvelFMNYvSoSbDnoThxfTIG9msj7WBh7iNa7mI8TXmvOegQtDWR011ZOo8dR3jnhWNH" + + "nSW4CRB7iSC5DMZ2y56dYS28XGBl01LYXF5ZTJJfLwQEhbRWSTdXIBJq07E0YxRu0SaLokA4uknOoicwXnD7LMCld4hFjuypYgWBuk" + + "3pC09CPA0MJjQNTTAvxGqDTqSWoXWDZRIMUWyGyz3FCkpPUjv4mIpVYt2bGl6cHsMBzVnpL6yXMCw2mNqJx8Rvi4gQaHH6LzvHbVKR" + + "w4kE53703DNOc8cA9Zc0efJa4NHOFxc4XmMOtjGW7vbWPp0CTVCJLG94ddSFJrimpamPM59bs12x2ih51EpOFR5ITIxJnd79HEkYDU" + + "xRIOuPIe4VpM01RnFN4g3ChDqmjQ03wQY9I8Mvh59u3MujggQfwAhCc84MAz0jVukoMfhAAhMNUPLuwRj0qpqr6B3DdKZ4KDFWF2Ga" + + "Iu1sEFlKvPdfF1uefbTj6YdjUciWu1UBH47VbIcTbvbwmUiu2javB21kOenyDoelK5GUM4u0uPeXIOOhtZsJb8kz88h1joWkaKr2fc" + + "jrIS08FM47Y4Z2Mi4zfwyN54L"; + + ProcessCredentialsProvider credentialsProvider = ProcessCredentialsProvider.builder() + .command(scriptLocation + " accessKeyId secretAccessKey " + LONG_SESSION_TOKEN) + .build(); + + AwsSessionCredentials sessionCredentials = (AwsSessionCredentials) credentialsProvider.resolveCredentials(); + + Assertions.assertThat(sessionCredentials.accessKeyId()).isEqualTo("accessKeyId"); + Assertions.assertThat(sessionCredentials.sessionToken()).isNotNull(); + } + public static String copyProcessCredentialsScript() { String scriptClasspathFilename = Platform.isWindows() ? "windows-credentials-script.bat" : "linux-credentials-script.sh"; - String scriptClasspathLocation = "/resources/process/" + scriptClasspathFilename; + String scriptClasspathLocation = PROCESS_RESOURCE_PATH + scriptClasspathFilename; InputStream scriptInputStream = null; OutputStream scriptOutputStream = null; diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProviderTest.java index 3de935acb09b..8be07bb42bf0 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/ProfileCredentialsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java index feefc4b1ded7..19c99236eee6 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/StaticCredentialsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java index e2f47fc1355c..de34421dee4b 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProviderTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProviderTest.java index f669402b1fac..25fcbd0f1f2a 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProviderTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/LazyAwsCredentialsProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtilsTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtilsTest.java index 9ab5e24161eb..eaf9a1f067e7 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtilsTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/ProfileCredentialsUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/AbstractAws4SignerTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/AbstractAws4SignerTest.java index 6176784ec531..9b028c2444bf 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/AbstractAws4SignerTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/AbstractAws4SignerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4EventStreamSignerTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4EventStreamSignerTest.java index c77d9fe0512f..eedded3748f9 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4EventStreamSignerTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4EventStreamSignerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4SignerTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4SignerTest.java index e36a52f3b22e..ef2ee593a995 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4SignerTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/Aws4SignerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/EventStreamAws4SignerTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/EventStreamAws4SignerTest.java new file mode 100644 index 000000000000..6c4b8bbec0a2 --- /dev/null +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/EventStreamAws4SignerTest.java @@ -0,0 +1,105 @@ +package software.amazon.awssdk.auth.signer; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +import io.reactivex.Flowable; +import java.net.URI; +import java.nio.ByteBuffer; +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import org.junit.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.signer.internal.SignerTestUtils; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.regions.Region; +import software.amazon.eventstream.HeaderValue; +import software.amazon.eventstream.Message; +import software.amazon.eventstream.MessageDecoder; + +public class EventStreamAws4SignerTest { + /** + * Verify that when an event stream is open from one day to the next, the signature is properly signed for the day of the + * event. + */ + @Test + public void openStreamEventSignaturesCanRollOverBetweenDays() { + EventStreamAws4Signer signer = EventStreamAws4Signer.create(); + + Region region = Region.US_WEST_2; + AwsCredentials credentials = AwsBasicCredentials.create("a", "s"); + String signingName = "name"; + AdjustableClock clock = new AdjustableClock(); + clock.time = Instant.parse("2020-01-01T23:59:59Z"); + + SdkHttpFullRequest initialRequest = SdkHttpFullRequest.builder() + .uri(URI.create("http://localhost:8080")) + .method(SdkHttpMethod.GET) + .build(); + SdkHttpFullRequest signedRequest = SignerTestUtils.signRequest(signer, initialRequest, credentials, signingName, clock, + region.id()); + + ByteBuffer event = new Message(Collections.emptyMap(), "foo".getBytes(UTF_8)).toByteBuffer(); + + Callable lastEvent = () -> { + clock.time = Instant.parse("2020-01-02T00:00:00Z"); + return event; + }; + + AsyncRequestBody requestBody = AsyncRequestBody.fromPublisher(Flowable.concatArray(Flowable.just(event), + Flowable.fromCallable(lastEvent))); + + AsyncRequestBody signedBody = SignerTestUtils.signAsyncRequest(signer, signedRequest, requestBody, credentials, + signingName, clock, region.id()); + + List signedMessages = readMessages(signedBody); + assertThat(signedMessages.size()).isEqualTo(3); + + Map firstMessageHeaders = signedMessages.get(0).getHeaders(); + assertThat(firstMessageHeaders.get(":date").getTimestamp()).isEqualTo("2020-01-01T23:59:59Z"); + assertThat(Base64.getEncoder().encodeToString(firstMessageHeaders.get(":chunk-signature").getByteArray())) + .isEqualTo("EFt7ZU043r/TJE8U+1GxJXscmNxoqmIdGtUIl8wE9u0="); + + Map lastMessageHeaders = signedMessages.get(2).getHeaders(); + assertThat(lastMessageHeaders.get(":date").getTimestamp()).isEqualTo("2020-01-02T00:00:00Z"); + assertThat(Base64.getEncoder().encodeToString(lastMessageHeaders.get(":chunk-signature").getByteArray())) + .isEqualTo("UTRGo0D7BQytiVkH1VofR/8f3uFsM4V5QR1A8grr1+M="); + + } + + private List readMessages(AsyncRequestBody signedBody) { + MessageDecoder decoder = new MessageDecoder(); + Flowable.fromPublisher(signedBody).blockingForEach(x -> decoder.feed(x.array())); + return decoder.getDecodedMessages(); + } + + private static class AdjustableClock extends Clock { + private Instant time; + + @Override + public ZoneId getZone() { + return ZoneOffset.UTC; + } + + @Override + public Clock withZone(ZoneId zone) { + throw new UnsupportedOperationException(); + } + + @Override + public Instant instant() { + return time; + } + } + +} \ No newline at end of file diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParamsTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParamsTest.java index 58eaeb33b388..8b470001fbb0 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParamsTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/Aws4SignerRequestParamsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4SignerTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4SignerTest.java new file mode 100644 index 000000000000..6e78ea6f8f79 --- /dev/null +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/BaseEventStreamAsyncAws4SignerTest.java @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.auth.signer.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import org.junit.BeforeClass; +import org.junit.Test; +import software.amazon.awssdk.utils.BinaryUtils; +import software.amazon.eventstream.HeaderValue; +import software.amazon.eventstream.Message; + +public class BaseEventStreamAsyncAws4SignerTest { + private static Map headers; + + @BeforeClass + public static void setup() { + headers = new LinkedHashMap<>(); + headers.put("header1", HeaderValue.fromInteger(42)); + headers.put("header2", HeaderValue.fromBoolean(false)); + headers.put("header3", HeaderValue.fromString("Hello world")); + } + + @Test + public void toDebugString_emptyPayload_generatesCorrectString() { + Message m = new Message(headers, new byte[0]); + + assertThat(BaseEventStreamAsyncAws4Signer.toDebugString(m, false)) + .isEqualTo("Message = {headers={header1={42}, header2={false}, header3={\"Hello world\"}}, payload=}"); + } + + @Test + public void toDebugString_noHeaders_emptyPayload_generatesCorrectString() { + Message m = new Message(new LinkedHashMap<>(), new byte[0]); + + assertThat(BaseEventStreamAsyncAws4Signer.toDebugString(m, false)) + .isEqualTo("Message = {headers={}, payload=}"); + } + + @Test + public void toDebugString_largePayload_truncate_generatesCorrectString() { + byte[] payload = new byte[128]; + new Random().nextBytes(payload); + Message m = new Message(headers, payload); + + byte[] first32 = Arrays.copyOf(payload, 32); + String expectedPayloadString = BinaryUtils.toHex(first32); + assertThat(BaseEventStreamAsyncAws4Signer.toDebugString(m, true)) + .isEqualTo("Message = {headers={header1={42}, header2={false}, header3={\"Hello world\"}}, payload=" + expectedPayloadString + "...}"); + } + + @Test + public void toDebugString_largePayload_noTruncate_generatesCorrectString() { + byte[] payload = new byte[128]; + new Random().nextBytes(payload); + Message m = new Message(headers, payload); + + String expectedPayloadString = BinaryUtils.toHex(payload); + assertThat(BaseEventStreamAsyncAws4Signer.toDebugString(m, false)) + .isEqualTo("Message = {headers={header1={42}, header2={false}, header3={\"Hello world\"}}, payload=" + expectedPayloadString + "}"); + } + + @Test + public void toDebugString_smallPayload_truncate_doesNotAddEllipsis() { + byte[] payload = new byte[8]; + new Random().nextBytes(payload); + Message m = new Message(headers, payload); + + String expectedPayloadString = BinaryUtils.toHex(payload); + assertThat(BaseEventStreamAsyncAws4Signer.toDebugString(m, true)) + .isEqualTo("Message = {headers={header1={42}, header2={false}, header3={\"Hello world\"}}, payload=" + expectedPayloadString + "}"); + } +} diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/FifoCacheTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/FifoCacheTest.java index 244e56185729..7988bc17db3e 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/FifoCacheTest.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/FifoCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerKeyTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerKeyTest.java new file mode 100644 index 000000000000..9100c163965c --- /dev/null +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerKeyTest.java @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.auth.signer.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import java.time.Instant; +import org.junit.Test; + +public class SignerKeyTest { + + @Test + public void isValidForDate_dayBefore_false() { + Instant signerDate = Instant.parse("2020-03-03T23:59:59Z"); + SignerKey key = new SignerKey(signerDate, new byte[0]); + Instant dayBefore = Instant.parse("2020-03-02T23:59:59Z"); + + assertThat(key.isValidForDate(dayBefore)).isFalse(); + } + + @Test + public void isValidForDate_sameDay_true() { + Instant signerDate = Instant.parse("2020-03-03T23:59:59Z"); + SignerKey key = new SignerKey(signerDate, new byte[0]); + Instant sameDay = Instant.parse("2020-03-03T01:02:03Z"); + + assertThat(key.isValidForDate(sameDay)).isTrue(); + } + + @Test + public void isValidForDate_dayAfter_false() { + Instant signerDate = Instant.parse("2020-03-03T23:59:59Z"); + SignerKey key = new SignerKey(signerDate, new byte[0]); + Instant dayAfter = Instant.parse("2020-03-04T00:00:00Z"); + + assertThat(key.isValidForDate(dayAfter)).isFalse(); + } +} diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerTestUtils.java b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerTestUtils.java index cb9bf935a674..9fc7be4e6fd0 100644 --- a/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerTestUtils.java +++ b/core/auth/src/test/java/software/amazon/awssdk/auth/signer/internal/SignerTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/resources/jetty-logging.properties b/core/auth/src/test/resources/jetty-logging.properties index 0b7cafa3b426..6d7baed3e770 100644 --- a/core/auth/src/test/resources/jetty-logging.properties +++ b/core/auth/src/test/resources/jetty-logging.properties @@ -1,3 +1,18 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file 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. +# + # Set up logging implementation org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.LEVEL=INFO diff --git a/core/auth/src/test/resources/log4j.properties b/core/auth/src/test/resources/log4j.properties index 63c957f7a291..f59ce7e9b749 100644 --- a/core/auth/src/test/resources/log4j.properties +++ b/core/auth/src/test/resources/log4j.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/core/auth/src/test/resources/resources/process/linux-credentials-script.sh b/core/auth/src/test/resources/resources/process/linux-credentials-script.sh index 9d2b51a171cb..03c802ec3034 100644 --- a/core/auth/src/test/resources/resources/process/linux-credentials-script.sh +++ b/core/auth/src/test/resources/resources/process/linux-credentials-script.sh @@ -4,8 +4,12 @@ echo '"Version": 1,'; echo "\"AccessKeyId\": \"$1\","; echo "\"SecretAccessKey\": \"$2\""; if [[ $# -ge 3 ]]; then - echo ',' - echo "\"SessionToken\": \"$RANDOM\""; + echo ','; + if [[ "$3" = "RANDOM_TOKEN" ]]; then + echo "\"SessionToken\": \"$RANDOM\"" + else + echo "\"SessionToken\": \"$3\"" + fi; fi; if [[ $# -ge 4 ]]; then echo ',' diff --git a/core/auth/src/test/resources/resources/process/windows-credentials-script.bat b/core/auth/src/test/resources/resources/process/windows-credentials-script.bat index c8cabe4967aa..bb5ed72f070c 100644 --- a/core/auth/src/test/resources/resources/process/windows-credentials-script.bat +++ b/core/auth/src/test/resources/resources/process/windows-credentials-script.bat @@ -6,7 +6,11 @@ ECHO "AccessKeyId": "%1", ECHO "SecretAccessKey": "%2" IF NOT "%3"=="" ( ECHO , - ECHO "SessionToken": "%RANDOM" + IF "%3"=="RANDOM_TOKEN" ( + ECHO "SessionToken": "%RANDOM%" + ) ELSE ( + ECHO "SessionToken": "%3" + ) ) IF NOT "%4"=="" ( ECHO , diff --git a/core/aws-core/pom.xml b/core/aws-core/pom.xml index c4ca3e133f76..911bb417db04 100644 --- a/core/aws-core/pom.xml +++ b/core/aws-core/pom.xml @@ -1,4 +1,19 @@ + + @@ -7,7 +22,7 @@ software.amazon.awssdk core - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT aws-core @@ -33,6 +48,11 @@ auth ${awsjavasdk.version} + + software.amazon.awssdk + profiles + ${awsjavasdk.version} + sdk-core software.amazon.awssdk diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsExecutionAttribute.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsExecutionAttribute.java index a58e8485a991..4071e0bec0ff 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsExecutionAttribute.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsExecutionAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; +import software.amazon.awssdk.awscore.client.config.AwsClientOption; import software.amazon.awssdk.core.interceptor.ExecutionAttribute; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; @@ -33,5 +34,11 @@ public final class AwsExecutionAttribute extends SdkExecutionAttribute { */ public static final ExecutionAttribute AWS_REGION = new ExecutionAttribute<>("AwsRegion"); - private AwsExecutionAttribute() {} + /** + * The {@link AwsClientOption#ENDPOINT_PREFIX} for the client. + */ + public static final ExecutionAttribute ENDPOINT_PREFIX = new ExecutionAttribute<>("AwsEndpointPrefix"); + + private AwsExecutionAttribute() { + } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequest.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequest.java index 455f114d3a24..e993a66cbc2c 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequest.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java index 17cb2b02f41f..da8f7e7abd92 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsRequestOverrideConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponse.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponse.java index 61659aa0cb30..15f21e97457c 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponse.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponseMetadata.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponseMetadata.java index 9b61de5a9b05..f9e326f62317 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponseMetadata.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/AwsResponseMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/DefaultAwsResponseMetadata.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/DefaultAwsResponseMetadata.java index 528792e54e2b..eaa8fa2fc2d5 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/DefaultAwsResponseMetadata.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/DefaultAwsResponseMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsAsyncClientBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsAsyncClientBuilder.java index 60d211345992..a318d2016c81 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsAsyncClientBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsAsyncClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsClientBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsClientBuilder.java index 1d086185633e..e8b22a8b28d7 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsClientBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java index 98b1ae31d582..d775c9016cb8 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package software.amazon.awssdk.awscore.client.builder; import java.net.URI; +import java.util.Collections; +import java.util.List; import java.util.Optional; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; @@ -23,20 +25,24 @@ import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.awscore.client.config.AwsAdvancedClientOption; import software.amazon.awssdk.awscore.client.config.AwsClientOption; -import software.amazon.awssdk.awscore.internal.EndpointUtils; +import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder; +import software.amazon.awssdk.awscore.interceptor.HelpfulUnknownHostExceptionInterceptor; import software.amazon.awssdk.awscore.retry.AwsRetryPolicy; import software.amazon.awssdk.core.client.builder.SdkDefaultClientBuilder; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.core.retry.RetryPolicy; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; -import software.amazon.awssdk.regions.providers.AwsRegionProvider; import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain; -import software.amazon.awssdk.regions.providers.LazyAwsRegionProvider; import software.amazon.awssdk.utils.AttributeMap; +import software.amazon.awssdk.utils.CollectionUtils; /** * An SDK-internal implementation of the methods in {@link AwsClientBuilder}, {@link AwsAsyncClientBuilder} and @@ -61,8 +67,6 @@ public abstract class AwsDefaultClientBuilder implements AwsClientBuilder { private static final String DEFAULT_ENDPOINT_PROTOCOL = "https"; - private static final AwsRegionProvider DEFAULT_REGION_PROVIDER = - new LazyAwsRegionProvider(DefaultAwsRegionProviderChain::new); protected AwsDefaultClientBuilder() { super(); @@ -108,13 +112,12 @@ protected AttributeMap serviceHttpConfig() { @Override protected final SdkClientConfiguration mergeChildDefaults(SdkClientConfiguration configuration) { SdkClientConfiguration config = mergeServiceDefaults(configuration); - return config.merge(c -> c.option(AwsClientOption.AWS_REGION, resolveRegion(config)) - .option(AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION, true) - .option(AwsClientOption.CREDENTIALS_PROVIDER, DefaultCredentialsProvider.create()) - .option(SdkClientOption.RETRY_POLICY, AwsRetryPolicy.defaultRetryPolicy()) + + return config.merge(c -> c.option(AwsAdvancedClientOption.ENABLE_DEFAULT_REGION_DETECTION, true) .option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION, false) .option(AwsClientOption.SERVICE_SIGNING_NAME, signingName()) - .option(SdkClientOption.SERVICE_NAME, serviceName())); + .option(SdkClientOption.SERVICE_NAME, serviceName()) + .option(AwsClientOption.ENDPOINT_PREFIX, serviceEndpointPrefix())); } /** @@ -126,11 +129,19 @@ protected SdkClientConfiguration mergeServiceDefaults(SdkClientConfiguration con @Override protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfiguration configuration) { - SdkClientConfiguration config = configuration.toBuilder() - .option(SdkClientOption.ENDPOINT, resolveEndpoint(configuration)) - .option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration)) - .build(); - return finalizeServiceConfiguration(config); + configuration = finalizeServiceConfiguration(configuration); + + configuration = configuration.toBuilder() + .option(AwsClientOption.AWS_REGION, resolveRegion(configuration)) + .build(); + + return configuration.toBuilder() + .option(AwsClientOption.CREDENTIALS_PROVIDER, resolveCredentials(configuration)) + .option(SdkClientOption.ENDPOINT, resolveEndpoint(configuration)) + .option(SdkClientOption.EXECUTION_INTERCEPTORS, addAwsInterceptors(configuration)) + .option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration)) + .option(SdkClientOption.RETRY_POLICY, resolveAwsRetryPolicy(configuration)) + .build(); } /** @@ -153,9 +164,15 @@ private Region resolveSigningRegion(SdkClientConfiguration config) { */ private URI resolveEndpoint(SdkClientConfiguration config) { return Optional.ofNullable(config.option(SdkClientOption.ENDPOINT)) - .orElseGet(() -> EndpointUtils.buildEndpoint(DEFAULT_ENDPOINT_PROTOCOL, - serviceEndpointPrefix(), - config.option(AwsClientOption.AWS_REGION))); + .orElseGet(() -> endpointFromConfig(config)); + } + + private URI endpointFromConfig(SdkClientConfiguration config) { + return new DefaultServiceEndpointBuilder(serviceEndpointPrefix(), DEFAULT_ENDPOINT_PROTOCOL) + .withRegion(config.option(AwsClientOption.AWS_REGION)) + .withProfileFile(config.option(SdkClientOption.PROFILE_FILE)) + .withProfileName(config.option(SdkClientOption.PROFILE_NAME)) + .getServiceEndpoint(); } /** @@ -175,7 +192,44 @@ private Region regionFromDefaultProvider(SdkClientConfiguration config) { if (defaultRegionDetectionEnabled != null && !defaultRegionDetectionEnabled) { throw new IllegalStateException("No region was configured, and use-region-provider-chain was disabled."); } - return DEFAULT_REGION_PROVIDER.getRegion(); + + ProfileFile profileFile = config.option(SdkClientOption.PROFILE_FILE); + String profileName = config.option(SdkClientOption.PROFILE_NAME); + return DefaultAwsRegionProviderChain.builder() + .profileFile(() -> profileFile) + .profileName(profileName) + .build() + .getRegion(); + } + + /** + * Resolve the credentials that should be used based on the customer's configuration. + */ + private AwsCredentialsProvider resolveCredentials(SdkClientConfiguration config) { + return config.option(AwsClientOption.CREDENTIALS_PROVIDER) != null + ? config.option(AwsClientOption.CREDENTIALS_PROVIDER) + : DefaultCredentialsProvider.builder() + .profileFile(config.option(SdkClientOption.PROFILE_FILE)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .build(); + } + + private RetryPolicy resolveAwsRetryPolicy(SdkClientConfiguration config) { + RetryPolicy policy = config.option(SdkClientOption.RETRY_POLICY); + + if (policy != null) { + if (policy.additionalRetryConditionsAllowed()) { + return AwsRetryPolicy.addRetryConditions(policy); + } else { + return policy; + } + } + + RetryMode retryMode = RetryMode.resolver() + .profileFile(() -> config.option(SdkClientOption.PROFILE_FILE)) + .profileName(config.option(SdkClientOption.PROFILE_NAME)) + .resolve(); + return AwsRetryPolicy.forRetryMode(retryMode); } @Override @@ -197,4 +251,14 @@ public final BuilderT credentialsProvider(AwsCredentialsProvider credentialsProv public final void setCredentialsProvider(AwsCredentialsProvider credentialsProvider) { credentialsProvider(credentialsProvider); } + + private List addAwsInterceptors(SdkClientConfiguration config) { + List interceptors = awsInterceptors(); + interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS)); + return interceptors; + } + + private List awsInterceptors() { + return Collections.singletonList(new HelpfulUnknownHostExceptionInterceptor()); + } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsSyncClientBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsSyncClientBuilder.java index 10aca94d429e..f4dbfdf4035f 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsSyncClientBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsSyncClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsAdvancedClientOption.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsAdvancedClientOption.java index 444460fbddfc..f004a74b5a9c 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsAdvancedClientOption.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsAdvancedClientOption.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsClientOption.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsClientOption.java index 3fba7909ad4e..44e6f69851c8 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsClientOption.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/config/AwsClientOption.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -45,6 +45,14 @@ public final class AwsClientOption extends ClientOption { */ public static final AwsClientOption SERVICE_SIGNING_NAME = new AwsClientOption<>(String.class); + /** + * The first part of the URL in the DNS name for the service. Eg. in the endpoint "dynamodb.amazonaws.com", this is the + * "dynamodb". + * + * For standard services, this should match the "endpointPrefix" field in the AWS model. + */ + public static final AwsClientOption ENDPOINT_PREFIX = new AwsClientOption<>(String.class); + private AwsClientOption(Class valueClass) { super(valueClass); } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsAsyncClientHandler.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsAsyncClientHandler.java index 8e8ddb510bf9..a731247f4f28 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsAsyncClientHandler.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsAsyncClientHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import software.amazon.awssdk.core.client.handler.ClientExecutionParams; import software.amazon.awssdk.core.client.handler.SdkAsyncClientHandler; import software.amazon.awssdk.core.http.ExecutionContext; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; /** * Async client handler for AWS SDK clients. @@ -60,8 +61,8 @@ public Complet @Override protected ExecutionContext createExecutionContext( - ClientExecutionParams executionParams) { - return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration); + ClientExecutionParams executionParams, ExecutionAttributes executionAttributes) { + return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration, executionAttributes); } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java index add11ba27c8e..eaaac5e52503 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; import software.amazon.awssdk.awscore.client.config.AwsAdvancedClientOption; import software.amazon.awssdk.awscore.client.config.AwsClientOption; +import software.amazon.awssdk.core.RequestOverrideConfiguration; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SdkResponse; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; @@ -57,7 +58,8 @@ private AwsClientHandlerUtils() { static ExecutionContext createExecutionContext( ClientExecutionParams executionParams, - SdkClientConfiguration clientConfig) { + SdkClientConfiguration clientConfig, + ExecutionAttributes executionAttributes) { SdkRequest originalRequest = executionParams.getInput(); AwsCredentialsProvider clientCredentials = clientConfig.option(AwsClientOption.CREDENTIALS_PROVIDER); @@ -71,17 +73,20 @@ static ExecutionContext Validate.validState(credentials != null, "Credential providers must never return null."); - ExecutionAttributes executionAttributes = new ExecutionAttributes() + executionAttributes .putAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG, clientConfig.option(SdkClientOption.SERVICE_CONFIGURATION)) .putAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS, credentials) .putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, clientConfig.option(AwsClientOption.SERVICE_SIGNING_NAME)) .putAttribute(AwsExecutionAttribute.AWS_REGION, clientConfig.option(AwsClientOption.AWS_REGION)) + .putAttribute(AwsExecutionAttribute.ENDPOINT_PREFIX, clientConfig.option(AwsClientOption.ENDPOINT_PREFIX)) .putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, clientConfig.option(AwsClientOption.SIGNING_REGION)) .putAttribute(SdkInternalExecutionAttribute.IS_FULL_DUPLEX, executionParams.isFullDuplex()) .putAttribute(SdkExecutionAttribute.CLIENT_TYPE, clientConfig.option(SdkClientOption.CLIENT_TYPE)) .putAttribute(SdkExecutionAttribute.SERVICE_NAME, clientConfig.option(SdkClientOption.SERVICE_NAME)) - .putAttribute(SdkExecutionAttribute.OPERATION_NAME, executionParams.getOperationName()); + .putAttribute(SdkExecutionAttribute.OPERATION_NAME, executionParams.getOperationName()) + .putAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN, + clientConfig.option(SdkClientOption.ENDPOINT_OVERRIDDEN)); ExecutionInterceptorChain executionInterceptorChain = new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS)); @@ -125,7 +130,7 @@ public static ByteBuffer encodeEventStreamRequestToByteBuffer(SdkHttpFullRequest private static Signer computeSigner(SdkRequest originalRequest, SdkClientConfiguration clientConfiguration) { return originalRequest.overrideConfiguration() - .flatMap(config -> config.signer()) + .flatMap(RequestOverrideConfiguration::signer) .orElse(clientConfiguration.option(AwsAdvancedClientOption.SIGNER)); } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsSyncClientHandler.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsSyncClientHandler.java index dd6f96414613..3edc19686430 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsSyncClientHandler.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsSyncClientHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -64,13 +64,19 @@ public ReturnT @Override protected ExecutionContext createExecutionContext( - ClientExecutionParams executionParams) { - return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration); + ClientExecutionParams executionParams, ExecutionAttributes executionAttributes) { + return AwsClientHandlerUtils.createExecutionContext(executionParams, clientConfiguration, executionAttributes); } private ClientExecutionParams addCrc32Validation( ClientExecutionParams executionParams) { - return executionParams.withResponseHandler(new Crc32ValidationResponseHandler<>(executionParams.getResponseHandler())); + if (executionParams.getCombinedResponseHandler() != null) { + return executionParams.withCombinedResponseHandler( + new Crc32ValidationResponseHandler<>(executionParams.getCombinedResponseHandler())); + } else { + return executionParams.withResponseHandler( + new Crc32ValidationResponseHandler<>(executionParams.getResponseHandler())); + } } /** diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java index e4cafc3c0c3b..4d7b2b2f1f68 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/endpoint/DefaultServiceEndpointBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import java.net.URISyntaxException; import software.amazon.awssdk.annotations.NotThreadSafe; import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.ServiceMetadata; import software.amazon.awssdk.utils.Validate; @@ -31,10 +32,12 @@ // TODO We may not need this anymore, we should default to AWS partition when resolving // a region we don't know about yet. public final class DefaultServiceEndpointBuilder { - private final String serviceName; private final String protocol; + private Region region; + private ProfileFile profileFile; + private String profileName; public DefaultServiceEndpointBuilder(String serviceName, String protocol) { this.serviceName = Validate.paramNotNull(serviceName, "serviceName"); @@ -49,8 +52,20 @@ public DefaultServiceEndpointBuilder withRegion(Region region) { return this; } + public DefaultServiceEndpointBuilder withProfileFile(ProfileFile profileFile) { + this.profileFile = profileFile; + return this; + } + + public DefaultServiceEndpointBuilder withProfileName(String profileName) { + this.profileName = profileName; + return this; + } + public URI getServiceEndpoint() { - ServiceMetadata serviceMetadata = ServiceMetadata.of(serviceName); + ServiceMetadata serviceMetadata = ServiceMetadata.of(serviceName) + .reconfigure(c -> c.profileFile(() -> profileFile) + .profileName(profileName)); return withProtocol(serviceMetadata.endpointFor(region)); } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/DefaultEventStreamResponseHandlerBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/DefaultEventStreamResponseHandlerBuilder.java index eb18057ce9b9..c5342aabd402 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/DefaultEventStreamResponseHandlerBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/DefaultEventStreamResponseHandlerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformer.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformer.java index 174378f43377..d801bb9c19e6 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformer.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandler.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandler.java index 77baff4cf6a6..093ab3420a75 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandler.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandlerFromBuilder.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandlerFromBuilder.java index ee1c48888afc..2cfe1bf6c8e4 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandlerFromBuilder.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamResponseHandlerFromBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionJsonMarshaller.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionJsonMarshaller.java index cc1dae4628ed..be7fa785cd4d 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionJsonMarshaller.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionJsonMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionPojoSupplier.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionPojoSupplier.java index ead629f4e9b0..f780ae9ecb20 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionPojoSupplier.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/EventStreamTaggedUnionPojoSupplier.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/RestEventStreamAsyncResponseTransformer.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/RestEventStreamAsyncResponseTransformer.java index aecadf150b45..5e9f84cea3f2 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/RestEventStreamAsyncResponseTransformer.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/eventstream/RestEventStreamAsyncResponseTransformer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,7 +17,6 @@ import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; - import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.SdkResponse; import software.amazon.awssdk.core.async.AsyncResponseTransformer; diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsErrorDetails.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsErrorDetails.java index 289d4a188238..8073839e45d6 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsErrorDetails.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsErrorDetails.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -204,7 +204,8 @@ protected static final class BuilderImpl implements Builder { private SdkHttpResponse sdkHttpResponse; private SdkBytes rawResponse; - private BuilderImpl() {} + private BuilderImpl() { + } private BuilderImpl(AwsErrorDetails awsErrorDetails) { this.errorMessage = awsErrorDetails.errorMessage(); diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsServiceException.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsServiceException.java index 05ffe3c1bf61..ac8146aef8fc 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsServiceException.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/exception/AwsServiceException.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/GlobalServiceExecutionInterceptor.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/GlobalServiceExecutionInterceptor.java index 244f85e73437..43f0f0566908 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/GlobalServiceExecutionInterceptor.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/GlobalServiceExecutionInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,41 +15,22 @@ package software.amazon.awssdk.awscore.interceptor; -import java.net.UnknownHostException; import software.amazon.awssdk.annotations.SdkProtectedApi; -import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.core.exception.SdkClientException; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; /** - * An interceptor that can be used for global services that will tell the customer when they're using a global service that - * doesn't support non-global regions. + * A more specific version of {@link HelpfulUnknownHostExceptionInterceptor} that was used for older IAM clients. This can be + * removed if we ever drop backwards-compatibility with older IAM client versions, because newer IAM client versions do not + * depend on this interceptor. */ @SdkProtectedApi -public final class GlobalServiceExecutionInterceptor implements ExecutionInterceptor { - @Override - public void onExecutionFailure(Context.FailedExecution context, ExecutionAttributes executionAttributes) { - if (hasCause(context.exception(), UnknownHostException.class) && - !executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION).isGlobalRegion()) { - throw SdkClientException.builder() - .message("This is a global service. Consider setting AWS_GLOBAL or another global " + - "region when creating your client.") - .cause(context.exception()) - .build(); - } - } - - private boolean hasCause(Throwable thrown, Class cause) { - if (thrown == null) { - return false; - } +public class GlobalServiceExecutionInterceptor implements ExecutionInterceptor { + private static final HelpfulUnknownHostExceptionInterceptor DELEGATE = new HelpfulUnknownHostExceptionInterceptor(); - if (cause.isAssignableFrom(thrown.getClass())) { - return true; - } - - return hasCause(thrown.getCause(), cause); + @Override + public Throwable modifyException(Context.FailedExecution context, ExecutionAttributes executionAttributes) { + return DELEGATE.modifyException(context, executionAttributes); } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java new file mode 100644 index 000000000000..2572e1b79f32 --- /dev/null +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptor.java @@ -0,0 +1,137 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.awscore.interceptor; + +import java.net.UnknownHostException; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.regions.PartitionMetadata; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.regions.RegionMetadata; +import software.amazon.awssdk.regions.ServiceMetadata; +import software.amazon.awssdk.regions.ServicePartitionMetadata; + +/** + * This interceptor will monitor for {@link UnknownHostException}s and provide the customer with additional information they can + * use to debug or fix the problem. + */ +@SdkInternalApi +public final class HelpfulUnknownHostExceptionInterceptor implements ExecutionInterceptor { + @Override + public Throwable modifyException(Context.FailedExecution context, ExecutionAttributes executionAttributes) { + if (!hasCause(context.exception(), UnknownHostException.class)) { + return context.exception(); + } + + StringBuilder error = new StringBuilder(); + error.append("Received an UnknownHostException when attempting to interact with a service. See cause for the " + + "exact endpoint that is failing to resolve. "); + + Optional globalRegionErrorDetails = getGlobalRegionErrorDetails(executionAttributes); + + if (globalRegionErrorDetails.isPresent()) { + error.append(globalRegionErrorDetails.get()); + } else { + error.append("If this is happening on an endpoint that previously worked, there may be a network connectivity " + + "issue or your DNS cache could be storing endpoints for too long."); + } + + return SdkClientException.builder().message(error.toString()).cause(context.exception()).build(); + } + + /** + * If the customer is interacting with a global service (one with a single endpoint/region for an entire partition), this + * will return error details that can instruct the customer on how to configure their client for success. + */ + private Optional getGlobalRegionErrorDetails(ExecutionAttributes executionAttributes) { + Region clientRegion = clientRegion(executionAttributes); + if (clientRegion.isGlobalRegion()) { + return Optional.empty(); + } + + List globalPartitionsForService = globalPartitionsForService(executionAttributes); + if (globalPartitionsForService.isEmpty()) { + return Optional.empty(); + } + + String clientPartition = Optional.ofNullable(clientRegion.metadata()) + .map(RegionMetadata::partition) + .map(PartitionMetadata::id) + .orElse(null); + + Optional globalRegionForClientRegion = + globalPartitionsForService.stream() + .filter(p -> p.partition().id().equals(clientPartition)) + .findAny() + .flatMap(ServicePartitionMetadata::globalRegion); + + if (!globalRegionForClientRegion.isPresent()) { + String globalRegionsForThisService = globalPartitionsForService.stream() + .map(ServicePartitionMetadata::globalRegion) + .filter(Optional::isPresent) + .map(Optional::get) + .filter(Region::isGlobalRegion) + .map(Region::id) + .collect(Collectors.joining("/")); + + return Optional.of("This specific service may be a global service, in which case you should configure a global " + + "region like " + globalRegionsForThisService + " on the client."); + } + + Region globalRegion = globalRegionForClientRegion.get(); + + return Optional.of("This specific service is global in the same partition as the region configured on this client (" + + clientRegion + "). If this is the first time you're trying to talk to this service in this region, " + + "you should try configuring the global region on your client, instead: " + globalRegion); + } + + /** + * Retrieve the region configured on the client. + */ + private Region clientRegion(ExecutionAttributes executionAttributes) { + return executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION); + } + + /** + * Retrieve all global partitions for the AWS service that we're interacting with. + */ + private List globalPartitionsForService(ExecutionAttributes executionAttributes) { + return ServiceMetadata.of(executionAttributes.getAttribute(AwsExecutionAttribute.ENDPOINT_PREFIX)) + .servicePartitions() + .stream() + .filter(sp -> sp.globalRegion().isPresent()) + .collect(Collectors.toList()); + } + + private boolean hasCause(Throwable thrown, Class cause) { + if (thrown == null) { + return false; + } + + if (cause.isAssignableFrom(thrown.getClass())) { + return true; + } + + return hasCause(thrown.getCause(), cause); + } +} diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsErrorCode.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsErrorCode.java index 5d632e4c125a..989450330004 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsErrorCode.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsErrorCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ public final class AwsErrorCode { throttlingErrorCodes.add("BandwidthLimitExceeded"); throttlingErrorCodes.add("RequestThrottled"); throttlingErrorCodes.add("RequestThrottledException"); + throttlingErrorCodes.add("EC2ThrottledException"); THROTTLING_ERROR_CODES = unmodifiableSet(throttlingErrorCodes); Set definiteClockSkewErrorCodes = new HashSet<>(3); @@ -60,6 +61,8 @@ public final class AwsErrorCode { Set retryableErrorCodes = new HashSet<>(1); retryableErrorCodes.add("PriorRequestNotComplete"); + retryableErrorCodes.add("RequestTimeout"); + retryableErrorCodes.add("RequestTimeoutException"); RETRYABLE_ERROR_CODES = unmodifiableSet(retryableErrorCodes); } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsStatusCode.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsStatusCode.java index 7a49baee3e5b..eaf254ddb8ab 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsStatusCode.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsStatusCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/EndpointUtils.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/EndpointUtils.java deleted file mode 100644 index 6736b1740777..000000000000 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/EndpointUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.awscore.internal; - -import java.net.URI; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.utils.Validate; - -/** - * A collection of utility methods centered around generating service endpoints from various pieces of information. - */ -@SdkInternalApi -public final class EndpointUtils { - private EndpointUtils() {} - - /** - * Generate an endpoint from the provided endpoint protocol, url prefix, and region. - * - * @param protocol The protocol that should be used when communicating with AWS (usually http or https). - * @param serviceEndpointPrefix The endpoint prefix that should be used when communicating with AWS (usually the - * endpointPrefix in the service's model). - * @param region The AWS region that should be communicated with. - * @return The AWS endpoint to use for communication. - */ - public static URI buildEndpoint(String protocol, String serviceEndpointPrefix, Region region) { - Validate.paramNotNull(protocol, "protocol"); - Validate.paramNotNull(serviceEndpointPrefix, "serviceEndpointPrefix"); - Validate.paramNotNull(region, "region"); - return new DefaultServiceEndpointBuilder(serviceEndpointPrefix, protocol).withRegion(region).getServiceEndpoint(); - } -} diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/client/config/AwsClientOptionValidation.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/client/config/AwsClientOptionValidation.java index 2c9fc5e77478..5dce8619099a 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/client/config/AwsClientOptionValidation.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/client/config/AwsClientOptionValidation.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -27,7 +27,8 @@ */ @SdkInternalApi public final class AwsClientOptionValidation extends SdkClientOptionValidation { - private AwsClientOptionValidation() {} + private AwsClientOptionValidation() { + } public static void validateAsyncClientOptions(SdkClientConfiguration c) { validateClientOptions(c); diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignRequest.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignRequest.java index c815ce7ae224..9e24358a47f7 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignRequest.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -79,7 +79,8 @@ public int hashCode() { protected abstract static class DefaultBuilder> implements Builder { private Duration signatureDuration; - protected DefaultBuilder() {} + protected DefaultBuilder() { + } protected DefaultBuilder(PresignRequest request) { this.signatureDuration = request.signatureDuration; diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignedRequest.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignedRequest.java index 4ea652cbcd88..f3da23bf11f5 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignedRequest.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/PresignedRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -184,7 +184,8 @@ protected abstract static class DefaultBuilder> impl private SdkBytes signedPayload; private SdkHttpRequest httpRequest; - protected DefaultBuilder() {} + protected DefaultBuilder() { + } protected DefaultBuilder(PresignedRequest request) { this.expiration = request.expiration; diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/SdkPresigner.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/SdkPresigner.java index 1c25896d8ba6..83d513174d8f 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/SdkPresigner.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/presigner/SdkPresigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicy.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicy.java index 5b9fc17e6a88..4899a2601b02 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicy.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicy.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.awscore.internal.AwsErrorCode; import software.amazon.awssdk.awscore.retry.conditions.RetryOnErrorCodeCondition; +import software.amazon.awssdk.core.retry.RetryMode; import software.amazon.awssdk.core.retry.RetryPolicy; import software.amazon.awssdk.core.retry.conditions.OrRetryCondition; import software.amazon.awssdk.core.retry.conditions.RetryCondition; @@ -31,12 +32,38 @@ public final class AwsRetryPolicy { private AwsRetryPolicy() { } + /** + * Retrieve the {@link RetryCondition#defaultRetryCondition()} with AWS-specific conditions added. + */ public static RetryCondition defaultRetryCondition() { - return OrRetryCondition.create(RetryCondition.defaultRetryCondition(), - RetryOnErrorCodeCondition.create(AwsErrorCode.RETRYABLE_ERROR_CODES)); + return OrRetryCondition.create(RetryCondition.defaultRetryCondition(), awsRetryCondition()); } + /** + * Retrieve the {@link RetryPolicy#defaultRetryPolicy()} with AWS-specific conditions added. + */ public static RetryPolicy defaultRetryPolicy() { - return RetryPolicy.defaultRetryPolicy().toBuilder().retryCondition(defaultRetryCondition()).build(); + return forRetryMode(RetryMode.defaultRetryMode()); + } + + /** + * Retrieve the {@link RetryPolicy#defaultRetryPolicy()} with AWS-specific conditions added. This uses the specified + * {@link RetryMode} when constructing the {@link RetryPolicy}. + */ + public static RetryPolicy forRetryMode(RetryMode retryMode) { + return addRetryConditions(RetryPolicy.forRetryMode(retryMode)); + } + + /** + * Update the provided {@link RetryPolicy} to add AWS-specific conditions. + */ + public static RetryPolicy addRetryConditions(RetryPolicy condition) { + return condition.toBuilder() + .retryCondition(OrRetryCondition.create(condition.retryCondition(), awsRetryCondition())) + .build(); + } + + private static RetryOnErrorCodeCondition awsRetryCondition() { + return RetryOnErrorCodeCondition.create(AwsErrorCode.RETRYABLE_ERROR_CODES); } } diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/conditions/RetryOnErrorCodeCondition.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/conditions/RetryOnErrorCodeCondition.java index 8fd542c522e8..6673b13440f2 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/conditions/RetryOnErrorCodeCondition.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/retry/conditions/RetryOnErrorCodeCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHeader.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHeader.java index c4478847d5fb..bda478a7ea47 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHeader.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHeader.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHostNameUtils.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHostNameUtils.java index 16a5d6b8d930..eb44ac136c83 100644 --- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHostNameUtils.java +++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/util/AwsHostNameUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java index 9f26e87d55bb..c32600c5f014 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/builder/DefaultAwsClientBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java index e8c13d978c79..90fa1f40034d 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/endpoint/DefaultServiceEndpointBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/http/NoopTestAwsRequest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/http/NoopTestAwsRequest.java index d55afe982d78..a87238aafa71 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/http/NoopTestAwsRequest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/http/NoopTestAwsRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/HttpTestUtils.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/HttpTestUtils.java index 9fc12690cc00..e7ff085f290d 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/HttpTestUtils.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/HttpTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ import java.util.concurrent.Executors; import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.internal.http.AmazonSyncHttpClient; import software.amazon.awssdk.core.internal.http.loader.DefaultSdkHttpClientBuilder; import software.amazon.awssdk.core.retry.RetryPolicy; diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/ValidSdkObjects.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/ValidSdkObjects.java index df11173862b5..ac00131437b5 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/ValidSdkObjects.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/client/utils/ValidSdkObjects.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformerTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformerTest.java index 0995b0518872..d8ca1069a97b 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformerTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/eventstream/EventStreamAsyncResponseTransformerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/exception/AwsServiceExceptionTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/exception/AwsServiceExceptionTest.java index 11b8849f4d8b..b9e003a4c4e4 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/exception/AwsServiceExceptionTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/exception/AwsServiceExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java new file mode 100644 index 000000000000..471eedcff9b8 --- /dev/null +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/HelpfulUnknownHostExceptionInterceptorTest.java @@ -0,0 +1,118 @@ +package software.amazon.awssdk.awscore.interceptor; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.UnknownHostException; +import org.junit.Test; +import org.mockito.Mockito; +import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.interceptor.InterceptorContext; +import software.amazon.awssdk.core.internal.interceptor.DefaultFailedExecutionContext; +import software.amazon.awssdk.regions.Region; + +public class HelpfulUnknownHostExceptionInterceptorTest { + private static final ExecutionInterceptor INTERCEPTOR = new HelpfulUnknownHostExceptionInterceptor(); + + @Test + public void modifyException_skipsNonUnknownHostExceptions() { + IOException exception = new IOException(); + assertThat(modifyException(exception)).isEqualTo(exception); + } + + @Test + public void modifyException_supportsNestedUnknownHostExceptions() { + Exception exception = new UnknownHostException(); + exception.initCause(new IOException()); + exception = new IllegalArgumentException(exception); + exception = new UnsupportedOperationException(exception); + + assertThat(modifyException(exception, Region.AWS_GLOBAL)).isInstanceOf(SdkClientException.class); + } + + @Test + public void modifyException_returnsGenericHelp_forGlobalRegions() { + UnknownHostException exception = new UnknownHostException(); + assertThat(modifyException(exception, Region.AWS_GLOBAL)) + .isInstanceOf(SdkClientException.class) + .hasMessageContaining("network"); + } + + @Test + public void modifyException_returnsGenericHelp_forUnknownServices() { + UnknownHostException exception = new UnknownHostException(); + assertThat(modifyException(exception, Region.US_EAST_1, "millems-hotdog-stand")) + .isInstanceOf(SdkClientException.class) + .satisfies(t -> doesNotHaveMessageContaining(t, "global")) + .hasMessageContaining("network"); + } + + @Test + public void modifyException_returnsGenericHelp_forUnknownServicesInUnknownRegions() { + UnknownHostException exception = new UnknownHostException(); + assertThat(modifyException(exception, Region.of("cn-north-99"), "millems-hotdog-stand")) + .isInstanceOf(SdkClientException.class) + .satisfies(t -> doesNotHaveMessageContaining(t, "global")) + .hasMessageContaining("network"); + } + + @Test + public void modifyException_returnsGenericHelp_forServicesRegionalizedInAllPartitions() { + UnknownHostException exception = new UnknownHostException(); + assertThat(modifyException(exception, Region.US_EAST_1, "dynamodb")) + .isInstanceOf(SdkClientException.class) + .satisfies(t -> doesNotHaveMessageContaining(t, "global")) + .hasMessageContaining("network"); + } + + @Test + public void modifyException_returnsGenericGlobalRegionHelp_forServicesGlobalInSomePartitionOtherThanTheClientPartition() { + UnknownHostException exception = new UnknownHostException(); + assertThat(modifyException(exception, Region.of("cn-north-99"), "iam")) + .isInstanceOf(SdkClientException.class) + .satisfies(t -> doesNotHaveMessageContaining(t, "network")) + .hasMessageContaining("aws-global") + .hasMessageContaining("aws-cn-global"); + } + + @Test + public void modifyException_returnsSpecificGlobalRegionHelp_forServicesGlobalInTheClientRegionPartition() { + UnknownHostException exception = new UnknownHostException(); + assertThat(modifyException(exception, Region.of("cn-north-1"), "iam")) + .isInstanceOf(SdkClientException.class) + .satisfies(t -> doesNotHaveMessageContaining(t, "aws-global")) + .hasMessageContaining("aws-cn-global"); + } + + private void doesNotHaveMessageContaining(Throwable throwable, String value) { + assertThat(throwable.getMessage()).doesNotContain(value); + } + + private Throwable modifyException(Throwable throwable) { + return modifyException(throwable, null); + } + + private Throwable modifyException(Throwable throwable, Region clientRegion) { + return modifyException(throwable, clientRegion, null); + } + + private Throwable modifyException(Throwable throwable, Region clientRegion, String serviceEndpointPrefix) { + SdkRequest sdkRequest = Mockito.mock(SdkRequest.class); + + DefaultFailedExecutionContext context = + DefaultFailedExecutionContext.builder() + .interceptorContext(InterceptorContext.builder().request(sdkRequest).build()) + .exception(throwable) + .build(); + + ExecutionAttributes executionAttributes = + new ExecutionAttributes().putAttribute(AwsExecutionAttribute.AWS_REGION, clientRegion) + .putAttribute(AwsExecutionAttribute.ENDPOINT_PREFIX, serviceEndpointPrefix); + + return INTERCEPTOR.modifyException(context, executionAttributes); + } +} \ No newline at end of file diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicyTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicyTest.java index e3f287ad11d0..002e93f81633 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicyTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/AwsRetryPolicyTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -102,6 +102,17 @@ public void doesNotRetryOnNonRetryableErrorCode() { assertFalse(shouldRetry(applyErrorCode("ValidationError"))); } + @Test + public void retriesOnEC2ThrottledException() { + AwsServiceException ex = AwsServiceException.builder() + .awsErrorDetails(AwsErrorDetails.builder() + .errorCode("EC2ThrottledException") + .build()) + .build(); + + assertTrue(shouldRetry(b -> b.exception(ex))); + } + private boolean shouldRetry(Consumer builder) { return defaultRetryCondition().shouldRetry(RetryPolicyContext.builder().applyMutation(builder).build()); } diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/RetryOnErrorCodeConditionTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/RetryOnErrorCodeConditionTest.java index e9e17b15dd44..bb72774c6511 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/RetryOnErrorCodeConditionTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/retry/RetryOnErrorCodeConditionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/util/AwsHostNameUtilsTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/util/AwsHostNameUtilsTest.java index 265057f4445a..91a2cb1cfd90 100644 --- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/util/AwsHostNameUtilsTest.java +++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/util/AwsHostNameUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/pom.xml b/core/pom.xml index c9e8c36561be..76a1bd7b08aa 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,6 +1,6 @@ + protocols software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactory.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactory.java index 3a9c37bec1f5..86fc70e23fc2 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactory.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java index 76c48f341e3a..d9f9144d6bf2 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/AwsStructuredCborFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java index 002c1bea2aa8..0520530a5103 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkCborGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java index aca1307d771f..3e014774db63 100644 --- a/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java +++ b/core/protocols/aws-cbor-protocol/src/main/java/software/amazon/awssdk/protocols/cbor/internal/SdkStructuredCborFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-cbor-protocol/src/test/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactoryTest.java b/core/protocols/aws-cbor-protocol/src/test/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactoryTest.java index 4dcbc1a27df3..6cc6cbaf2ccd 100644 --- a/core/protocols/aws-cbor-protocol/src/test/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactoryTest.java +++ b/core/protocols/aws-cbor-protocol/src/test/java/software/amazon/awssdk/protocols/cbor/AwsCborProtocolFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/pom.xml b/core/protocols/aws-ion-protocol/pom.xml index a788cedd2a4b..e9be1f4696c2 100644 --- a/core/protocols/aws-ion-protocol/pom.xml +++ b/core/protocols/aws-ion-protocol/pom.xml @@ -1,11 +1,26 @@ + + protocols software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java index dff6a6b6f8e4..5e976913b51d 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/AwsIonProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java index a3e789b73444..85345e33245e 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/AwsStructuredIonFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java index 13b522378222..0d3bdb20aeb0 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/CompositeErrorCodeParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java index 50d59b5c8ede..64d91e3b080b 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonErrorCodeParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java index a1b1ab35a97e..7425516f5ee6 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java index 09e204e35dc2..f93568dd43af 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/IonParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java index 1c92a263ab3a..4d758970d2d2 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkIonGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java index fb04ff63011c..639ea5e60bbe 100644 --- a/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java +++ b/core/protocols/aws-ion-protocol/src/main/java/software/amazon/awssdk/protocols/ion/internal/SdkStructuredIonFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java index d9599cd682b1..2b2fab841717 100644 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java +++ b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/AwsStructuredIonFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java index edead0a92c31..31c9b82298a3 100644 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java +++ b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java index 337b12ac1e02..ca829c900c83 100644 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java +++ b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java index 895bf3f2bafa..ba5429205563 100644 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java +++ b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/IonRoundtripTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java index 63ddcc3d30f2..eabb349822ba 100644 --- a/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java +++ b/core/protocols/aws-ion-protocol/src/test/java/software/amazon/awssdk/protocols/ion/ValidSdkObjects.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/pom.xml b/core/protocols/aws-json-protocol/pom.xml index 36cf4510dd7c..d28646888042 100644 --- a/core/protocols/aws-json-protocol/pom.xml +++ b/core/protocols/aws-json-protocol/pom.xml @@ -1,11 +1,26 @@ + + protocols software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocol.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocol.java index c0188854aa63..98f923f0f114 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocol.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocol.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolFactory.java index 072ea031301f..fe6217c5f431 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolMetadata.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolMetadata.java index 130b2d8046f1..47ccb9491b80 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolMetadata.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/AwsJsonProtocolMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java index 0e183ad8ffdf..b85f58246e43 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsJsonProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java index 63819e81d98a..a27196287e4c 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/BaseAwsStructuredJsonFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/DefaultJsonContentTypeResolver.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/DefaultJsonContentTypeResolver.java index 24731d9d2307..12b6eda2b838 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/DefaultJsonContentTypeResolver.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/DefaultJsonContentTypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -32,6 +32,7 @@ public DefaultJsonContentTypeResolver(String prefix) { @Override public String resolveContentType(AwsJsonProtocolMetadata protocolMetadata) { + //Changing this to 'application/json' may break clients expecting 'application/x-amz-json-1.1' return prefix + protocolMetadata.protocolVersion(); } } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/ErrorCodeParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/ErrorCodeParser.java index 1143f2e20540..bb7fc7d487af 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/ErrorCodeParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/ErrorCodeParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java index 0889183932f1..bb5edba9ee11 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContent.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContentTypeResolver.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContentTypeResolver.java index 8d0ff1eb29bd..59e395af105b 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContentTypeResolver.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonContentTypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonOperationMetadata.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonOperationMetadata.java index 87074578b86a..874528a2babe 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonOperationMetadata.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/JsonOperationMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java index 559af86a646e..0e91bfbd019e 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/SdkJsonGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java index 2ef486ce682b..c88840d4d927 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonGenerator.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonGenerator.java index e411878f5f67..98b83699849c 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonGenerator.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/StructuredJsonGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -32,8 +32,6 @@ public interface StructuredJsonGenerator { */ StructuredJsonGenerator NO_OP = new StructuredJsonGenerator() { - private final byte[] emptyBytes = new byte[0]; - @Override public StructuredJsonGenerator writeStartArray() { return this; @@ -126,7 +124,7 @@ public StructuredJsonGenerator writeNumber(String number) { @Override public byte[] getBytes() { - return emptyBytes; + return null; } @Override diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java index ec1efc9166d0..14d97b854627 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/AwsStructuredPlainJsonFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java index 1392ceba41fa..914376cd4db1 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java index 6088da13dab3..2f2521946205 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkArrayNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java index ea70f1beabe5..1d2d413df357 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkEmbeddedObject.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java index 366b5e6e50c2..83a93e7370b5 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkJsonNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java index 40056340b43c..3d8739cd4345 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkNullNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java index e8cc7d2a8fad..59ab40887a02 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkObjectNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java index b75aed0d9456..98c1eb862b7e 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/dom/SdkScalarNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/HeaderMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/HeaderMarshaller.java index fee5eaa946b4..c8af60708ac0 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/HeaderMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/HeaderMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshaller.java index facfa5e7ab17..7b6ba85abbc5 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -24,14 +24,11 @@ * * @param Type to marshall. */ +@FunctionalInterface @SdkInternalApi public interface JsonMarshaller extends Marshaller { - JsonMarshaller NULL = new JsonMarshaller() { - @Override - public void marshall(Void val, JsonMarshallerContext context, String paramName, SdkField sdkField) { - } - }; + JsonMarshaller NULL = (val, context, paramName, sdkField) -> { }; /** * Marshall the data into the request. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerContext.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerContext.java index 15c4f97da95a..3d4581ed4a58 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerContext.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerRegistry.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerRegistry.java index 3c50226973c6..a95beb6e2760 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerRegistry.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonMarshallerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshaller.java index eefe150a73ff..18adefe81d61 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -201,9 +201,12 @@ private SdkHttpFullRequest finishMarshalling() { } byte[] content = jsonGenerator.getBytes(); - request.contentStreamProvider(() -> new ByteArrayInputStream(content)); - if (content.length > 0) { - request.putHeader(CONTENT_LENGTH, Integer.toString(content.length)); + + if (content != null) { + request.contentStreamProvider(() -> new ByteArrayInputStream(content)); + if (content.length > 0) { + request.putHeader(CONTENT_LENGTH, Integer.toString(content.length)); + } } } @@ -214,7 +217,7 @@ private SdkHttpFullRequest finishMarshalling() { if (!request.headers().containsKey(CONTENT_TYPE) && !hasEvent) { if (hasEventStreamingInput) { request.putHeader(CONTENT_TYPE, MIMETYPE_EVENT_STREAM); - } else if (contentType != null && !hasStreamingInput) { + } else if (contentType != null && !hasStreamingInput && request.contentStreamProvider() != null) { request.putHeader(CONTENT_TYPE, contentType); } } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshallerBuilder.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshallerBuilder.java index c8b32bfb9719..1a4446d4776a 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshallerBuilder.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/JsonProtocolMarshallerBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/NullAsEmptyBodyProtocolRequestMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/NullAsEmptyBodyProtocolRequestMarshaller.java index 26fb8c7ab0c0..9b3c444ef1fa 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/NullAsEmptyBodyProtocolRequestMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/NullAsEmptyBodyProtocolRequestMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/QueryParamMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/QueryParamMarshaller.java index d857e4d4feee..3d491fb6f392 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/QueryParamMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/QueryParamMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypeJsonMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypeJsonMarshaller.java index c269e52e5101..756691904224 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypeJsonMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypeJsonMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -93,34 +93,31 @@ public void marshall(Boolean val, StructuredJsonGenerator jsonGenerator, JsonMar } }; - public static final JsonMarshaller INSTANT = new JsonMarshaller() { - @Override - public void marshall(Instant val, JsonMarshallerContext context, String paramName, SdkField sdkField) { - StructuredJsonGenerator jsonGenerator = context.jsonGenerator(); - if (paramName != null) { - jsonGenerator.writeFieldName(paramName); - } - TimestampFormatTrait trait = sdkField.getTrait(TimestampFormatTrait.class); - if (trait != null) { - switch (trait.format()) { - case UNIX_TIMESTAMP: - jsonGenerator.writeNumber(DateUtils.formatUnixTimestampInstant(val)); - break; - case RFC_822: - jsonGenerator.writeValue(DateUtils.formatRfc1123Date(val)); - break; - case ISO_8601: - jsonGenerator.writeValue(DateUtils.formatIso8601Date(val)); - break; - default: - throw SdkClientException.create("Unrecognized timestamp format - " + trait.format()); - } - } else { - // Important to fallback to the jsonGenerator implementation as that may differ per wire format, - // irrespective of protocol. I.E. CBOR would default to unix timestamp as milliseconds while JSON - // will default to unix timestamp as seconds with millisecond decimal precision. - jsonGenerator.writeValue(val); + public static final JsonMarshaller INSTANT = (val, context, paramName, sdkField) -> { + StructuredJsonGenerator jsonGenerator = context.jsonGenerator(); + if (paramName != null) { + jsonGenerator.writeFieldName(paramName); + } + TimestampFormatTrait trait = sdkField.getTrait(TimestampFormatTrait.class); + if (trait != null) { + switch (trait.format()) { + case UNIX_TIMESTAMP: + jsonGenerator.writeNumber(DateUtils.formatUnixTimestampInstant(val)); + break; + case RFC_822: + jsonGenerator.writeValue(DateUtils.formatRfc1123Date(val)); + break; + case ISO_8601: + jsonGenerator.writeValue(DateUtils.formatIso8601Date(val)); + break; + default: + throw SdkClientException.create("Unrecognized timestamp format - " + trait.format()); } + } else { + // Important to fallback to the jsonGenerator implementation as that may differ per wire format, + // irrespective of protocol. I.E. CBOR would default to unix timestamp as milliseconds while JSON + // will default to unix timestamp as seconds with millisecond decimal precision. + jsonGenerator.writeValue(val); } }; diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypePathMarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypePathMarshaller.java index e3d4810a06a5..8b48a9ab1b61 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypePathMarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/marshall/SimpleTypePathMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java index f9941934d51f..954504d7d25f 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonErrorMessageParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java index f9c883a391ae..ac1de74e5c37 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonProtocolErrorUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonResponseHandler.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonResponseHandler.java index 41f4b04e086c..0c20eafc4d2f 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonResponseHandler.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/AwsJsonResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java index ef1b164f44f2..9eab0e57cd36 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/ErrorMessageParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java index 8c029f2f7cfb..f7dbbdf266ad 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/HeaderUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java index 706c44e3cab7..dcaf190e3565 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonErrorCodeParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java index f3c06838bbed..68aa66bd8a5c 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonProtocolUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -121,11 +121,11 @@ private static SdkPojo unmarshallStructured(JsonUnmarshallerContext context, Sdk if (jsonContent == null || jsonContent.isNull()) { return null; } - SdkField valueInfo = field.getTrait(MapTrait.class).valueFieldInfo(); + SdkField valueInfo = field.getTrait(MapTrait.class).valueFieldInfo(); Map map = new HashMap<>(); jsonContent.fields().forEach((fieldName, value) -> { JsonUnmarshaller unmarshaller = context.getUnmarshaller(valueInfo.location(), valueInfo.marshallingType()); - map.put(fieldName, unmarshaller.unmarshall(context, value, (SdkField) valueInfo)); + map.put(fieldName, unmarshaller.unmarshall(context, value, valueInfo)); }); return map; } @@ -137,10 +137,10 @@ private static List unmarshallList(JsonUnmarshallerContext context, SdkJsonNo return jsonContent.items() .stream() .map(item -> { - SdkField memberInfo = field.getTrait(ListTrait.class).memberFieldInfo(); + SdkField memberInfo = field.getTrait(ListTrait.class).memberFieldInfo(); JsonUnmarshaller unmarshaller = context.getUnmarshaller(memberInfo.location(), memberInfo.marshallingType()); - return unmarshaller.unmarshall(context, item, (SdkField) memberInfo); + return unmarshaller.unmarshall(context, item, memberInfo); }) .collect(Collectors.toList()); } diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonResponseHandler.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonResponseHandler.java index ff044e2f8668..fbf0e001069f 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonResponseHandler.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java index 64ce4e65e599..b71e0adbbdac 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerContext.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerContext.java index d5a49627627e..e740350fc0ab 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerContext.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerRegistry.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerRegistry.java index 9bbd66cae0e3..ff461163f5d3 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerRegistry.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/JsonUnmarshallerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java index c747f5991c64..02fac7fb163b 100644 --- a/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java +++ b/core/protocols/aws-json-protocol/src/main/java/software/amazon/awssdk/protocols/json/internal/unmarshall/SdkJsonErrorMessageParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java index b454876d192a..073ba292b3ca 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/AwsJsonErrorMessageParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java index df433959049a..9c4b94faee02 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/JsonErrorCodeParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java index 058e1cf01e02..7a81210c06fe 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/SdkJsonGeneratorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/ValidSdkObjects.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/ValidSdkObjects.java index ba368c8e9a28..4fbb78221816 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/ValidSdkObjects.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/ValidSdkObjects.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java index 96aa0b4cf508..ab8ae765a369 100644 --- a/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java +++ b/core/protocols/aws-json-protocol/src/test/java/software/amazon/awssdk/protocols/json/internal/dom/JsonDomParserTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.json.internal.dom; diff --git a/core/protocols/aws-query-protocol/pom.xml b/core/protocols/aws-query-protocol/pom.xml index 935d10a19e2d..fcb7686f5da5 100644 --- a/core/protocols/aws-query-protocol/pom.xml +++ b/core/protocols/aws-query-protocol/pom.xml @@ -1,11 +1,26 @@ + + protocols software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsEc2ProtocolFactory.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsEc2ProtocolFactory.java index 3c6e5536b426..072b696587dc 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsEc2ProtocolFactory.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsEc2ProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsQueryProtocolFactory.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsQueryProtocolFactory.java index e097f849d623..ae2891123282 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsQueryProtocolFactory.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/AwsQueryProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptor.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptor.java new file mode 100644 index 000000000000..3c8b5f7663a9 --- /dev/null +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptor.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.query.interceptor; + +import static java.util.Collections.singletonList; +import static software.amazon.awssdk.utils.StringUtils.lowerCase; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.utils.CollectionUtils; +import software.amazon.awssdk.utils.http.SdkHttpUtils; + +/** + * Modifies an HTTP request by moving query parameters to the body under the following conditions: + * - It is a POST request + * - There is no content stream provider + * - There are query parameters to transfer + *

    + * This interceptor is automatically inserted by codegen for services using Query Protocol + */ +@SdkProtectedApi +public final class QueryParametersToBodyInterceptor implements ExecutionInterceptor { + + private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=" + + lowerCase(StandardCharsets.UTF_8.toString()); + + @Override + public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, + ExecutionAttributes executionAttributes) { + + SdkHttpRequest httpRequest = context.httpRequest(); + + if (!(httpRequest instanceof SdkHttpFullRequest)) { + return httpRequest; + } + + SdkHttpFullRequest httpFullRequest = (SdkHttpFullRequest) httpRequest; + if (shouldPutParamsInBody(httpFullRequest)) { + return changeQueryParametersToFormData(httpFullRequest); + } + return httpFullRequest; + } + + private boolean shouldPutParamsInBody(SdkHttpFullRequest input) { + return input.method() == SdkHttpMethod.POST && + !input.contentStreamProvider().isPresent() && + !CollectionUtils.isNullOrEmpty(input.rawQueryParameters()); + } + + private SdkHttpRequest changeQueryParametersToFormData(SdkHttpFullRequest input) { + byte[] params = SdkHttpUtils.encodeAndFlattenFormData(input.rawQueryParameters()).orElse("") + .getBytes(StandardCharsets.UTF_8); + + return input.toBuilder().clearQueryParameters() + .contentStreamProvider(() -> new ByteArrayInputStream(params)) + .putHeader("Content-Length", singletonList(String.valueOf(params.length))) + .putHeader("Content-Type", singletonList(DEFAULT_CONTENT_TYPE)) + .build(); + } + +} diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java index d0d76d03cee8..3acd1e9d5288 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/ListQueryMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/MapQueryMarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/MapQueryMarshaller.java index bb58cc5f98f7..81951313ed87 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/MapQueryMarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/MapQueryMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshaller.java index bf6046a1c531..a6ee83b905b1 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ * * @param Type being marshalled. */ +@FunctionalInterface @SdkInternalApi public interface QueryMarshaller extends Marshaller { diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerContext.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerContext.java index 36e933968fdd..9a8babbdfdce 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerContext.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerRegistry.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerRegistry.java index b5bbdcc49d1b..55e15d405d88 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerRegistry.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryMarshallerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryProtocolMarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryProtocolMarshaller.java index e6298d62c093..518ed1685e54 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryProtocolMarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/QueryProtocolMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/SimpleTypeQueryMarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/SimpleTypeQueryMarshaller.java index ff2c4f506bba..98fcdb53ea9d 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/SimpleTypeQueryMarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/marshall/SimpleTypeQueryMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsQueryResponseHandler.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsQueryResponseHandler.java index 6d52c5471f6f..1df64688999b 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsQueryResponseHandler.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsQueryResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsXmlErrorUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsXmlErrorUnmarshaller.java new file mode 100644 index 000000000000..1ff4f6f44ce7 --- /dev/null +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/AwsXmlErrorUnmarshaller.java @@ -0,0 +1,232 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.query.internal.unmarshall; + +import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; + +import java.time.Duration; +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.exception.AwsErrorDetails; +import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.protocols.core.ExceptionMetadata; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; +import software.amazon.awssdk.protocols.query.unmarshall.XmlErrorUnmarshaller; + +/** + * Unmarshalls an AWS XML exception from parsed XML. + */ +@SdkInternalApi +public final class AwsXmlErrorUnmarshaller { + private static final String X_AMZN_REQUEST_ID_HEADER = "x-amzn-RequestId"; + + private final List exceptions; + private final Supplier defaultExceptionSupplier; + + private final XmlErrorUnmarshaller errorUnmarshaller; + + private AwsXmlErrorUnmarshaller(Builder builder) { + this.exceptions = builder.exceptions; + this.errorUnmarshaller = builder.errorUnmarshaller; + this.defaultExceptionSupplier = builder.defaultExceptionSupplier; + } + + /** + * @return New Builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Unmarshal an AWS XML exception + * @param documentRoot The parsed payload document + * @param errorRoot The specific element of the parsed payload document that contains the error to be marshalled + * or empty if it could not be located. + * @param documentBytes The raw bytes of the original payload document if they are available + * @param response The HTTP response object + * @param executionAttributes {@link ExecutionAttributes} for the current execution + * @return An {@link AwsServiceException} unmarshalled from the XML. + */ + public AwsServiceException unmarshall(XmlElement documentRoot, + Optional errorRoot, + Optional documentBytes, + SdkHttpFullResponse response, + ExecutionAttributes executionAttributes) { + String errorCode = getErrorCode(errorRoot); + + AwsServiceException.Builder builder = errorRoot + .map(e -> invokeSafely(() -> unmarshallFromErrorCode(response, e, errorCode))) + .orElseGet(this::defaultException); + + AwsErrorDetails awsErrorDetails = + AwsErrorDetails.builder() + .errorCode(errorCode) + .errorMessage(builder.message()) + .rawResponse(documentBytes.orElse(null)) + .sdkHttpResponse(response) + .serviceName(executionAttributes.getAttribute(AwsExecutionAttribute.SERVICE_NAME)) + .build(); + + builder.requestId(getRequestId(response, documentRoot)) + .statusCode(response.statusCode()) + .clockSkew(getClockSkew(executionAttributes)) + .awsErrorDetails(awsErrorDetails); + + return builder.build(); + } + + private Duration getClockSkew(ExecutionAttributes executionAttributes) { + Integer timeOffset = executionAttributes.getAttribute(SdkExecutionAttribute.TIME_OFFSET); + return timeOffset == null ? null : Duration.ofSeconds(timeOffset); + } + + /** + * @return Builder for the default service exception. Used when the error code doesn't match + * any known modeled exception or when we can't determine the error code. + */ + private AwsServiceException.Builder defaultException() { + return (AwsServiceException.Builder) defaultExceptionSupplier.get(); + } + + /** + * Unmarshalls the XML into the appropriate modeled exception based on the error code. If the error code + * is not present or does not match any known exception we unmarshall into the base service exception. + * + * @param errorRoot Root of element. Contains any modeled fields of the exception. + * @param errorCode Error code identifying the modeled exception. + * @return Unmarshalled exception builder. + */ + private AwsServiceException.Builder unmarshallFromErrorCode(SdkHttpFullResponse response, + XmlElement errorRoot, + String errorCode) { + SdkPojo sdkPojo = exceptions.stream() + .filter(e -> e.errorCode().equals(errorCode)) + .map(ExceptionMetadata::exceptionBuilderSupplier) + .findAny() + .orElse(defaultExceptionSupplier) + .get(); + + AwsServiceException.Builder builder = + ((AwsServiceException) errorUnmarshaller.unmarshall(sdkPojo, errorRoot, response)).toBuilder(); + builder.message(getMessage(errorRoot)); + return builder; + } + + /** + * Extracts the error code (used to identify the modeled exception) from the + * element. + * + * @param errorRoot Error element root. + * @return Error code or null if not present. + */ + private String getErrorCode(Optional errorRoot) { + return errorRoot.map(e -> e.getOptionalElementByName("Code") + .map(XmlElement::textContent) + .orElse(null)) + .orElse(null); + } + + /** + * Extracts the error message from the XML document. The message is in the + * element for all services. + * + * @param errorRoot Error element root. + * @return Error message or null if not present. + */ + private String getMessage(XmlElement errorRoot) { + return errorRoot.getOptionalElementByName("Message") + .map(XmlElement::textContent) + .orElse(null); + } + + /** + * Extracts the request ID from the XML document. Request ID is a top level element + * for all protocols, it may be RequestId or RequestID depending on the service. + * + * @param document Root XML document. + * @return Request ID string or null if not present. + */ + private String getRequestId(SdkHttpFullResponse response, XmlElement document) { + XmlElement requestId = document.getOptionalElementByName("RequestId") + .orElse(document.getElementByName("RequestID")); + return requestId != null ? + requestId.textContent() : + response.firstMatchingHeader(X_AMZN_REQUEST_ID_HEADER).orElse(null); + } + + /** + * Builder for {@link AwsXmlErrorUnmarshaller}. + */ + public static final class Builder { + + private List exceptions; + private Supplier defaultExceptionSupplier; + private XmlErrorUnmarshaller errorUnmarshaller; + + private Builder() { + } + + /** + * List of {@link ExceptionMetadata} to represent the modeled exceptions for the service. + * For AWS services the error type is a string representing the type of the modeled exception. + * + * @return This builder for method chaining. + */ + public Builder exceptions(List exceptions) { + this.exceptions = exceptions; + return this; + } + + /** + * Default exception type if "error code" does not match any known modeled exception. This is the generated + * base exception for the service (i.e. DynamoDbException). + * + * @return This builder for method chaining. + */ + public Builder defaultExceptionSupplier(Supplier defaultExceptionSupplier) { + this.defaultExceptionSupplier = defaultExceptionSupplier; + return this; + } + + /** + * The unmarshaller to use. The unmarshaller only unmarshalls any modeled fields of the exception, + * additional metadata is extracted by {@link AwsXmlErrorUnmarshaller}. + * + * @param errorUnmarshaller Error unmarshaller to use. + * @return This builder for method chaining. + */ + public Builder errorUnmarshaller(XmlErrorUnmarshaller errorUnmarshaller) { + this.errorUnmarshaller = errorUnmarshaller; + return this; + } + + /** + * @return New instance of {@link AwsXmlErrorUnmarshaller}. + */ + public AwsXmlErrorUnmarshaller build() { + return new AwsXmlErrorUnmarshaller(this); + } + } +} diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/ListQueryUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/ListQueryUnmarshaller.java index a0a550fafcec..fdbaf536b063 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/ListQueryUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/ListQueryUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/MapQueryUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/MapQueryUnmarshaller.java index fb1c33522c4c..9f7b36e276d6 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/MapQueryUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/MapQueryUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryProtocolUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryProtocolUnmarshaller.java index 3442d5c6d920..8b4359bb8cdc 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryProtocolUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryProtocolUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshaller.java index 1b99e23c424b..0fac5dfb2882 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerContext.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerContext.java index e29409b5082f..3b1c77955af9 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerContext.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerRegistry.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerRegistry.java index a4ad50f5f53c..89fbbffaa163 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerRegistry.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/QueryUnmarshallerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/SimpleTypeQueryUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/SimpleTypeQueryUnmarshaller.java index 04dc56da9583..161649126bbe 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/SimpleTypeQueryUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/internal/unmarshall/SimpleTypeQueryUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/AwsXmlErrorProtocolUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/AwsXmlErrorProtocolUnmarshaller.java index 873ee9965c5f..009f6a14195a 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/AwsXmlErrorProtocolUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/AwsXmlErrorProtocolUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,25 +15,21 @@ package software.amazon.awssdk.protocols.query.unmarshall; -import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; - import java.io.IOException; -import java.time.Duration; import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkProtectedApi; -import software.amazon.awssdk.awscore.AwsExecutionAttribute; import software.amazon.awssdk.awscore.exception.AwsErrorDetails; import software.amazon.awssdk.awscore.exception.AwsServiceException; import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.core.SdkPojo; import software.amazon.awssdk.core.http.HttpResponseHandler; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; -import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.http.SdkHttpFullResponse; import software.amazon.awssdk.protocols.core.ExceptionMetadata; +import software.amazon.awssdk.protocols.query.internal.unmarshall.AwsXmlErrorUnmarshaller; import software.amazon.awssdk.utils.Pair; /** @@ -86,17 +82,16 @@ @SdkProtectedApi public final class AwsXmlErrorProtocolUnmarshaller implements HttpResponseHandler { - private final List exceptions; - private final Supplier defaultExceptionSupplier; + private final AwsXmlErrorUnmarshaller awsXmlErrorUnmarshaller; private final Function> errorRootExtractor; - private final XmlErrorUnmarshaller errorUnmarshaller; - private AwsXmlErrorProtocolUnmarshaller(Builder builder) { - this.exceptions = builder.exceptions; this.errorRootExtractor = builder.errorRootExtractor; - this.errorUnmarshaller = builder.errorUnmarshaller; - this.defaultExceptionSupplier = builder.defaultExceptionSupplier; + this.awsXmlErrorUnmarshaller = AwsXmlErrorUnmarshaller.builder() + .defaultExceptionSupplier(builder.defaultExceptionSupplier) + .exceptions(builder.exceptions) + .errorUnmarshaller(builder.errorUnmarshaller) + .build(); } @Override @@ -104,32 +99,8 @@ public AwsServiceException handle(SdkHttpFullResponse response, ExecutionAttribu Pair xmlAndBytes = parseXml(response); XmlElement document = xmlAndBytes.left(); Optional errorRoot = errorRootExtractor.apply(document); - String errorCode = getErrorCode(errorRoot); - - AwsServiceException.Builder builder = errorRoot - .map(e -> invokeSafely(() -> unmarshallFromErrorCode(response, e, errorCode))) - .orElseGet(this::defaultException); - - AwsErrorDetails awsErrorDetails = - AwsErrorDetails.builder() - .errorCode(errorCode) - .errorMessage(builder.message()) - .rawResponse(xmlAndBytes.right()) - .sdkHttpResponse(response) - .serviceName(executionAttributes.getAttribute(AwsExecutionAttribute.SERVICE_NAME)) - .build(); - - builder.requestId(getRequestId(response, document)) - .statusCode(response.statusCode()) - .clockSkew(getClockSkew(executionAttributes)) - .awsErrorDetails(awsErrorDetails); - - return builder.build(); - } - - private Duration getClockSkew(ExecutionAttributes executionAttributes) { - Integer timeOffset = executionAttributes.getAttribute(SdkExecutionAttribute.TIME_OFFSET); - return timeOffset == null ? null : Duration.ofSeconds(timeOffset); + return awsXmlErrorUnmarshaller.unmarshall(document, errorRoot, Optional.of(xmlAndBytes.right()), response, + executionAttributes); } /** @@ -173,79 +144,6 @@ private SdkBytes emptyXmlBytes() { return SdkBytes.fromUtf8String(""); } - /** - * @return Builder for the default service exception. Used when the error code doesn't match - * any known modeled exception or when we can't determine the error code. - */ - private AwsServiceException.Builder defaultException() { - return (AwsServiceException.Builder) defaultExceptionSupplier.get(); - } - - /** - * Unmarshalls the XML into the appropriate modeled exception based on the error code. If the error code - * is not present or does not match any known exception we unmarshall into the base service exception. - * - * @param errorRoot Root of element. Contains any modeled fields of the exception. - * @param errorCode Error code identifying the modeled exception. - * @return Unmarshalled exception builder. - */ - private AwsServiceException.Builder unmarshallFromErrorCode(SdkHttpFullResponse response, - XmlElement errorRoot, - String errorCode) { - SdkPojo sdkPojo = exceptions.stream() - .filter(e -> e.errorCode().equals(errorCode)) - .map(ExceptionMetadata::exceptionBuilderSupplier) - .findAny() - .orElse(defaultExceptionSupplier) - .get(); - - AwsServiceException.Builder builder = - ((AwsServiceException) errorUnmarshaller.unmarshall(sdkPojo, errorRoot, response)).toBuilder(); - builder.message(getMessage(errorRoot)); - return builder; - } - - /** - * Extracts the error code (used to identify the modeled exception) from the - * element. - * - * @param errorRoot Error element root. - * @return Error code or null if not present. - */ - private String getErrorCode(Optional errorRoot) { - return errorRoot.map(e -> e.getOptionalElementByName("Code") - .map(XmlElement::textContent) - .orElse(null)) - .orElse(null); - } - - /** - * Extracts the error message from the XML document. The message is in the - * element for all services. - * - * @param errorRoot Error element root. - * @return Error message or null if not present. - */ - private String getMessage(XmlElement errorRoot) { - return errorRoot.getOptionalElementByName("Message") - .map(XmlElement::textContent) - .orElse(null); - } - - /** - * Extracts the request ID from the XML document. Request ID is a top level element - * for all protocols, it may be RequestId or RequestID depending on the service. - * - * @param document Root XML document. - * @return Request ID string or null if not present. - */ - private String getRequestId(SdkHttpFullResponse response, XmlElement document) { - XmlElement requestId = document.getOptionalElementByName("RequestId") - .orElse(document.getElementByName("RequestID")); - return requestId != null ? - requestId.textContent() : - response.firstMatchingHeader(X_AMZN_REQUEST_ID_HEADER).orElse(null); - } /** * @return New Builder instance. diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlDomParser.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlDomParser.java index 25a56400ce17..59be4ec94c33 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlDomParser.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlDomParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,9 +16,13 @@ package software.amazon.awssdk.protocols.query.unmarshall; import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import software.amazon.awssdk.annotations.SdkProtectedApi; @@ -59,6 +63,11 @@ public static XmlElement parse(InputStream inputStream) { private static XmlElement parseElement(StartElement startElement, XMLEventReader reader) throws XMLStreamException { XmlElement.Builder elementBuilder = XmlElement.builder() .elementName(startElement.getName().getLocalPart()); + + if (startElement.getAttributes().hasNext()) { + parseAttributes(startElement, elementBuilder); + } + XMLEvent nextEvent; do { nextEvent = reader.nextEvent(); @@ -71,6 +80,21 @@ private static XmlElement parseElement(StartElement startElement, XMLEventReader return elementBuilder.build(); } + /** + * Parse the attributes of the element. + */ + @SuppressWarnings("unchecked") + private static void parseAttributes(StartElement startElement, XmlElement.Builder elementBuilder) { + Iterator iterator = startElement.getAttributes(); + Map attributes = new HashMap<>(); + iterator.forEachRemaining(a -> { + String key = a.getName().getPrefix() + ":" + a.getName().getLocalPart(); + attributes.put(key, a.getValue()); + }); + + elementBuilder.attributes(attributes); + } + /** * Reads all characters until the next end element event. * diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlElement.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlElement.java index 1fc044fd464f..ecb8a3ed1eff 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlElement.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlElement.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.core.exception.SdkClientException; @@ -35,12 +36,14 @@ public final class XmlElement { private final HashMap> childrenByElement; private final List children; private final String textContent; + private final Map attributes; private XmlElement(Builder builder) { this.elementName = builder.elementName; this.childrenByElement = new HashMap<>(builder.childrenByElement); this.children = Collections.unmodifiableList(new ArrayList<>(builder.children)); this.textContent = builder.textContent; + this.attributes = Collections.unmodifiableMap(new HashMap<>(builder.attributes)); } /** @@ -109,6 +112,20 @@ public String textContent() { return textContent; } + /** + * Retrieves an optional attribute by attribute name. + */ + public Optional getOptionalAttributeByName(String attribute) { + return Optional.ofNullable(attributes.get(attribute)); + } + + /** + * Retrieves the attributes associated with the element + */ + public Map attributes() { + return attributes; + } + /** * @return New {@link Builder} instance. */ @@ -129,9 +146,10 @@ public static XmlElement empty() { public static final class Builder { private String elementName; - private final HashMap> childrenByElement = new HashMap<>(); + private final Map> childrenByElement = new HashMap<>(); private final List children = new ArrayList<>(); private String textContent = ""; + private Map attributes = new HashMap<>(); private Builder() { } @@ -153,6 +171,11 @@ public Builder textContent(String textContent) { return this; } + public Builder attributes(Map attributes) { + this.attributes = attributes; + return this; + } + public XmlElement build() { return new XmlElement(this); } diff --git a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlErrorUnmarshaller.java b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlErrorUnmarshaller.java index 4613e081dd5c..1c03c36a8970 100644 --- a/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlErrorUnmarshaller.java +++ b/core/protocols/aws-query-protocol/src/main/java/software/amazon/awssdk/protocols/query/unmarshall/XmlErrorUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/XmlDomParserTest.java b/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/XmlDomParserTest.java index 640cde5eacc6..16ea0957c23e 100644 --- a/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/XmlDomParserTest.java +++ b/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/XmlDomParserTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.query; @@ -33,6 +48,22 @@ public void simpleXmlDocument_ParsedCorrectly() { .isEqualTo("42"); } + @Test + public void xmlWithAttributes_ParsedCorrectly() { + String xml = "" + + "" + + " stringVal" + + ""; + XmlElement element = XmlDomParser.parse(new StringInputStream(xml)); + assertThat(element.elementName()).isEqualTo("Struct"); + assertThat(element.children()).hasSize(1); + assertThat(element.getElementsByName("stringMember")) + .hasSize(1); + assertThat(element.attributes()).hasSize(2); + assertThat(element.getOptionalAttributeByName("xsi:type").get()).isEqualTo("foo"); + assertThat(element.getOptionalAttributeByName("xsi:nil").get()).isEqualTo("bar"); + } + @Test public void multipleElementsWithSameName_ParsedCorrectly() { String xml = "" diff --git a/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptorTest.java b/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptorTest.java new file mode 100644 index 000000000000..2398ab806589 --- /dev/null +++ b/core/protocols/aws-query-protocol/src/test/java/software/amazon/awssdk/protocols/query/interceptor/QueryParametersToBodyInterceptorTest.java @@ -0,0 +1,154 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.query.interceptor; + +import org.junit.Before; +import org.junit.Test; +import software.amazon.awssdk.core.Protocol; +import software.amazon.awssdk.core.SdkRequest; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.http.ContentStreamProvider; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.utils.IoUtils; + +import java.io.ByteArrayInputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.stream.Stream; + +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +public class QueryParametersToBodyInterceptorTest { + + public static final URI HTTP_LOCALHOST = URI.create("http://localhost:8080"); + + private QueryParametersToBodyInterceptor interceptor; + private ExecutionAttributes executionAttributes; + + private SdkHttpFullRequest.Builder requestBuilder; + + @Before + public void setup() { + + interceptor = new QueryParametersToBodyInterceptor(); + executionAttributes = new ExecutionAttributes(); + + requestBuilder = SdkHttpFullRequest.builder() + .protocol(Protocol.HTTPS.toString()) + .method(SdkHttpMethod.POST) + .putRawQueryParameter("key", singletonList("value")) + .uri(HTTP_LOCALHOST); + } + + @Test + public void postRequestsWithNoBodyHaveTheirParametersMovedToTheBody() throws Exception { + + SdkHttpFullRequest request = requestBuilder.build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(0); + assertThat(output.headers()) + .containsKey("Content-Length") + .containsEntry("Content-Type", singletonList("application/x-www-form-urlencoded; charset=utf-8")); + assertThat(output.contentStreamProvider()).isNotEmpty(); + } + + @Test + public void nonPostRequestsWithNoBodyAreUnaltered() throws Exception { + Stream.of(SdkHttpMethod.values()) + .filter(m -> !m.equals(SdkHttpMethod.POST)) + .forEach(this::nonPostRequestsUnaltered); + } + + @Test + public void postWithContentIsUnaltered() throws Exception { + byte[] contentBytes = "hello".getBytes(StandardCharsets.UTF_8); + ContentStreamProvider contentProvider = () -> new ByteArrayInputStream(contentBytes); + + SdkHttpFullRequest request = requestBuilder.contentStreamProvider(contentProvider).build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(1); + assertThat(output.headers()).hasSize(0); + assertThat(IoUtils.toByteArray(output.contentStreamProvider().get().newStream())).isEqualTo(contentBytes); + } + + @Test + public void onlyAlterRequestsIfParamsArePresent() throws Exception { + SdkHttpFullRequest request = requestBuilder.clearQueryParameters().build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(0); + assertThat(output.headers()).hasSize(0); + assertThat(output.contentStreamProvider()).isEmpty(); + } + + private void nonPostRequestsUnaltered(SdkHttpMethod method) { + + SdkHttpFullRequest request = requestBuilder.method(method).build(); + + SdkHttpFullRequest output = (SdkHttpFullRequest) interceptor.modifyHttpRequest( + new HttpRequestOnlyContext(request, null), executionAttributes); + + assertThat(output.rawQueryParameters()).hasSize(1); + assertThat(output.headers()).hasSize(0); + assertThat(output.contentStreamProvider()).isEmpty(); + } + + public final class HttpRequestOnlyContext implements software.amazon.awssdk.core.interceptor.Context.ModifyHttpRequest { + + private final SdkHttpRequest request; + private final RequestBody requestBody; + + public HttpRequestOnlyContext(SdkHttpRequest request, + RequestBody requestBody) { + this.request = request; + this.requestBody = requestBody; + } + + @Override + public SdkRequest request() { + return null; + } + + @Override + public SdkHttpRequest httpRequest() { + return request; + } + + @Override + public Optional requestBody() { + return Optional.ofNullable(requestBody); + } + + @Override + public Optional asyncRequestBody() { + return Optional.empty(); + } + } +} diff --git a/core/protocols/aws-xml-protocol/pom.xml b/core/protocols/aws-xml-protocol/pom.xml index 0d522f35a8c3..f5aec0f4d18c 100644 --- a/core/protocols/aws-xml-protocol/pom.xml +++ b/core/protocols/aws-xml-protocol/pom.xml @@ -1,11 +1,26 @@ + + protocols software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 @@ -61,6 +76,25 @@ assertj-core test + + org.mockito + mockito-core + test + - + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.protocols.xml + + + + + + diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsS3ProtocolFactory.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsS3ProtocolFactory.java index 24390da1e211..448f994fff09 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsS3ProtocolFactory.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsS3ProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,8 +16,15 @@ package software.amazon.awssdk.protocols.xml; import java.util.Optional; +import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.awscore.AwsResponse; +import software.amazon.awssdk.core.Response; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.core.http.HttpResponseHandler; import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; +import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlPredicatedResponseHandler; +import software.amazon.awssdk.protocols.xml.internal.unmarshall.DecorateErrorFromResponseBodyUnmarshaller; /** * Factory to generate the various protocol handlers and generators to be used for communicating with @@ -25,7 +32,6 @@ */ @SdkProtectedApi public final class AwsS3ProtocolFactory extends AwsXmlProtocolFactory { - private AwsS3ProtocolFactory(Builder builder) { super(builder); } @@ -57,4 +63,21 @@ public AwsS3ProtocolFactory build() { return new AwsS3ProtocolFactory(this); } } + + @Override + public HttpResponseHandler> createCombinedResponseHandler( + Supplier pojoSupplier, XmlOperationMetadata staxOperationMetadata) { + + return createErrorCouldBeInBodyResponseHandler(pojoSupplier, staxOperationMetadata); + } + + private HttpResponseHandler> createErrorCouldBeInBodyResponseHandler( + Supplier pojoSupplier, XmlOperationMetadata staxOperationMetadata) { + + return new AwsXmlPredicatedResponseHandler<>(r -> pojoSupplier.get(), + createResponseTransformer(pojoSupplier), + createErrorTransformer(), + DecorateErrorFromResponseBodyUnmarshaller.of(this::getErrorRoot), + staxOperationMetadata.isHasStreamingSuccessResponse()); + } } diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsXmlProtocolFactory.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsXmlProtocolFactory.java index 0bf4377430b8..c771e82f7193 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsXmlProtocolFactory.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/AwsXmlProtocolFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,14 +20,17 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.function.Function; import java.util.function.Supplier; import software.amazon.awssdk.annotations.SdkProtectedApi; import software.amazon.awssdk.awscore.AwsResponse; import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.Response; import software.amazon.awssdk.core.SdkPojo; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.http.HttpResponseHandler; +import software.amazon.awssdk.core.internal.http.CombinedResponseHandler; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.protocols.core.ExceptionMetadata; import software.amazon.awssdk.protocols.core.OperationInfo; @@ -37,7 +40,10 @@ import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; import software.amazon.awssdk.protocols.xml.internal.marshall.XmlGenerator; import software.amazon.awssdk.protocols.xml.internal.marshall.XmlProtocolMarshaller; +import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlErrorTransformer; import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlResponseHandler; +import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlResponseTransformer; +import software.amazon.awssdk.protocols.xml.internal.unmarshall.AwsXmlUnmarshallingContext; import software.amazon.awssdk.protocols.xml.internal.unmarshall.XmlProtocolUnmarshaller; /** @@ -62,6 +68,8 @@ public class AwsXmlProtocolFactory { public static final OperationMetadataAttribute ROOT_MARSHALL_LOCATION_ATTRIBUTE = new OperationMetadataAttribute<>(String.class); + private static final XmlProtocolUnmarshaller XML_PROTOCOL_UNMARSHALLER = XmlProtocolUnmarshaller.create(); + private final List modeledExceptions; private final Supplier defaultServiceExceptionSupplier; private final AwsXmlErrorProtocolUnmarshaller errorUnmarshaller; @@ -75,13 +83,13 @@ public class AwsXmlProtocolFactory { .builder() .defaultExceptionSupplier(defaultServiceExceptionSupplier) .exceptions(modeledExceptions) - .errorUnmarshaller(XmlProtocolUnmarshaller.builder().build()) + .errorUnmarshaller(XML_PROTOCOL_UNMARSHALLER) .errorRootExtractor(this::getErrorRoot) .build(); } /** - * Creates an instance of {@link XmlProtocolMarshaller} to be used for marshalling the requess. + * Creates an instance of {@link XmlProtocolMarshaller} to be used for marshalling the request. * * @param operationInfo Info required to marshall the request */ @@ -96,14 +104,36 @@ public ProtocolMarshaller createProtocolMarshaller(Operation public HttpResponseHandler createResponseHandler(Supplier pojoSupplier, XmlOperationMetadata staxOperationMetadata) { return new AwsXmlResponseHandler<>( - XmlProtocolUnmarshaller.builder().build(), r -> pojoSupplier.get(), + XML_PROTOCOL_UNMARSHALLER, r -> pojoSupplier.get(), staxOperationMetadata.isHasStreamingSuccessResponse()); } + protected Function createResponseTransformer( + Supplier pojoSupplier) { + + return new AwsXmlResponseTransformer<>( + XML_PROTOCOL_UNMARSHALLER, r -> pojoSupplier.get()); + } + + protected Function createErrorTransformer() { + return AwsXmlErrorTransformer.builder() + .defaultExceptionSupplier(defaultServiceExceptionSupplier) + .exceptions(modeledExceptions) + .errorUnmarshaller(XML_PROTOCOL_UNMARSHALLER) + .build(); + } + public HttpResponseHandler createErrorResponseHandler() { return errorUnmarshaller; } + public HttpResponseHandler> createCombinedResponseHandler( + Supplier pojoSupplier, XmlOperationMetadata staxOperationMetadata) { + + return new CombinedResponseHandler<>(createResponseHandler(pojoSupplier, staxOperationMetadata), + createErrorResponseHandler()); + } + /** * Extracts the element from the root XML document. This method is protected as S3 has * a slightly different location. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/XmlOperationMetadata.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/XmlOperationMetadata.java index b43d33f065e4..dfda61a4528e 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/XmlOperationMetadata.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/XmlOperationMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/HeaderMarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/HeaderMarshaller.java index b5fc8259cdab..8ad1c8264242 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/HeaderMarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/HeaderMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/QueryParamMarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/QueryParamMarshaller.java index c72837240fc8..f7f905892af6 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/QueryParamMarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/QueryParamMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/SimpleTypePathMarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/SimpleTypePathMarshaller.java index 2f8454be8e30..9e2620ee5e93 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/SimpleTypePathMarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/SimpleTypePathMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlGenerator.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlGenerator.java index fd927784dc09..3ce773f633f0 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlGenerator.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshaller.java index 458572480c09..e26bd4b38f19 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ * * @param Type to marshall. */ +@FunctionalInterface @SdkInternalApi public interface XmlMarshaller extends Marshaller { diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerContext.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerContext.java index 00d03de296d8..7e7b3bf45035 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerContext.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerRegistry.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerRegistry.java index bc2fa3a7e346..adf8bef921ae 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerRegistry.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlMarshallerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlPayloadMarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlPayloadMarshaller.java index fd97b9abe580..b7a78f3ea4ca 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlPayloadMarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlPayloadMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlProtocolMarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlProtocolMarshaller.java index 868ef9e54eee..10b07ea0d795 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlProtocolMarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlProtocolMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlWriter.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlWriter.java index 37e3b1909e27..4ef99766def8 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlWriter.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/marshall/XmlWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlErrorTransformer.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlErrorTransformer.java new file mode 100644 index 000000000000..d5a3a2f304c0 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlErrorTransformer.java @@ -0,0 +1,116 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import java.util.List; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Supplier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.awscore.exception.AwsServiceException; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.protocols.core.ExceptionMetadata; +import software.amazon.awssdk.protocols.query.internal.unmarshall.AwsXmlErrorUnmarshaller; +import software.amazon.awssdk.protocols.query.unmarshall.XmlErrorUnmarshaller; + +/** + * A transformer function that takes a parsed XML response and converts it into an {@link AwsServiceException}. Used + * as a component in the {@link AwsXmlPredicatedResponseHandler}. + */ +@SdkInternalApi +public final class AwsXmlErrorTransformer + implements Function { + + private final AwsXmlErrorUnmarshaller awsXmlErrorUnmarshaller; + + private AwsXmlErrorTransformer(Builder builder) { + this.awsXmlErrorUnmarshaller = AwsXmlErrorUnmarshaller.builder() + .defaultExceptionSupplier(builder.defaultExceptionSupplier) + .exceptions(builder.exceptions) + .errorUnmarshaller(builder.errorUnmarshaller) + .build(); + } + + @Override + public AwsServiceException apply(AwsXmlUnmarshallingContext context) { + return awsXmlErrorUnmarshaller.unmarshall(context.parsedRootXml(), + Optional.ofNullable(context.parsedErrorXml()), + Optional.empty(), + context.sdkHttpFullResponse(), + context.executionAttributes()); + } + + /** + * @return New Builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link AwsXmlErrorTransformer}. + */ + public static final class Builder { + + private List exceptions; + private Supplier defaultExceptionSupplier; + private XmlErrorUnmarshaller errorUnmarshaller; + + private Builder() { + } + + /** + * List of {@link ExceptionMetadata} to represent the modeled exceptions for the service. + * For AWS services the error type is a string representing the type of the modeled exception. + * + * @return This builder for method chaining. + */ + public Builder exceptions(List exceptions) { + this.exceptions = exceptions; + return this; + } + + /** + * Default exception type if "error code" does not match any known modeled exception. This is the generated + * base exception for the service (i.e. DynamoDbException). + * + * @return This builder for method chaining. + */ + public Builder defaultExceptionSupplier(Supplier defaultExceptionSupplier) { + this.defaultExceptionSupplier = defaultExceptionSupplier; + return this; + } + + /** + * The unmarshaller to use. The unmarshaller only unmarshalls any modeled fields of the exception, + * additional metadata is extracted by {@link AwsXmlErrorTransformer}. + * + * @param errorUnmarshaller Error unmarshaller to use. + * @return This builder for method chaining. + */ + public Builder errorUnmarshaller(XmlErrorUnmarshaller errorUnmarshaller) { + this.errorUnmarshaller = errorUnmarshaller; + return this; + } + + /** + * @return New instance of {@link AwsXmlErrorTransformer}. + */ + public AwsXmlErrorTransformer build() { + return new AwsXmlErrorTransformer(this); + } + } +} diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlPredicatedResponseHandler.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlPredicatedResponseHandler.java new file mode 100644 index 000000000000..d3d255496669 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlPredicatedResponseHandler.java @@ -0,0 +1,180 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import java.util.Optional; +import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.Response; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.core.SdkStandardLogger; +import software.amazon.awssdk.core.exception.RetryableException; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.http.HttpResponseHandler; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; +import software.amazon.awssdk.utils.IoUtils; + +/** + * Unmarshalls an HTTP response into either a successful response POJO, or into a (possibly modeled) exception based + * on a predicate that the unmarshalled response can be tested against. Returns a wrapper {@link Response} object which + * may contain either the unmarshalled success POJO, or the unmarshalled exception. + * + * @param Type of successful unmarshalled POJO. + */ +@SdkInternalApi +public class AwsXmlPredicatedResponseHandler implements HttpResponseHandler> { + private static final Logger log = LoggerFactory.getLogger(AwsXmlPredicatedResponseHandler.class); + + private final Function pojoSupplier; + private final Function successResponseTransformer; + private final Function errorResponseTransformer; + private final Function decorateContextWithError; + private final boolean needsConnectionLeftOpen; + + /** + * Standard constructor + * @param pojoSupplier A method that supplies an empty builder of the correct type + * @param successResponseTransformer A function that can unmarshall a response object from parsed XML + * @param errorResponseTransformer A function that can unmarshall an exception object from parsed XML + * @param decorateContextWithError A function that determines if the response was an error or not + * @param needsConnectionLeftOpen true if the underlying connection should not be closed once parsed + */ + public AwsXmlPredicatedResponseHandler( + Function pojoSupplier, + Function successResponseTransformer, + Function errorResponseTransformer, + Function decorateContextWithError, + boolean needsConnectionLeftOpen) { + + this.pojoSupplier = pojoSupplier; + this.successResponseTransformer = successResponseTransformer; + this.errorResponseTransformer = errorResponseTransformer; + this.decorateContextWithError = decorateContextWithError; + this.needsConnectionLeftOpen = needsConnectionLeftOpen; + } + + /** + * Handle a response + * @param httpResponse The HTTP response object + * @param executionAttributes The attributes attached to this particular execution. + * @return A wrapped response object with the unmarshalled result in it. + */ + @Override + public Response handle(SdkHttpFullResponse httpResponse, ExecutionAttributes executionAttributes) { + boolean didRequestFail = true; + try { + Response response = handleResponse(httpResponse, executionAttributes); + didRequestFail = !response.isSuccess(); + return response; + } finally { + closeInputStreamIfNeeded(httpResponse, didRequestFail); + } + } + + private Response handleResponse(SdkHttpFullResponse httpResponse, + ExecutionAttributes executionAttributes) { + + AwsXmlUnmarshallingContext parsedResponse = parseResponse(httpResponse, executionAttributes); + parsedResponse = decorateContextWithError.apply(parsedResponse); + + if (parsedResponse.isResponseSuccess()) { + OutputT response = handleSuccessResponse(parsedResponse); + return Response.builder().httpResponse(httpResponse) + .response(response) + .isSuccess(true) + .build(); + } else { + return Response.builder().httpResponse(httpResponse) + .exception(handleErrorResponse(parsedResponse)) + .isSuccess(false) + .build(); + } + } + + private AwsXmlUnmarshallingContext parseResponse(SdkHttpFullResponse httpFullResponse, + ExecutionAttributes executionAttributes) { + XmlElement document = XmlResponseParserUtils.parse(pojoSupplier.apply(httpFullResponse), httpFullResponse); + + return AwsXmlUnmarshallingContext.builder() + .parsedXml(document) + .executionAttributes(executionAttributes) + .sdkHttpFullResponse(httpFullResponse) + .build(); + } + + /** + * Handles a successful response from a service call by unmarshalling the results using the + * specified response handler. + * + * @return The contents of the response, unmarshalled using the specified response handler. + */ + private OutputT handleSuccessResponse(AwsXmlUnmarshallingContext parsedResponse) { + try { + SdkStandardLogger.REQUEST_LOGGER.debug(() -> "Received successful response: " + + parsedResponse.sdkHttpFullResponse().statusCode()); + return successResponseTransformer.apply(parsedResponse); + } catch (RetryableException e) { + throw e; + } catch (Exception e) { + if (e instanceof SdkException && ((SdkException) e).retryable()) { + throw (SdkException) e; + } + + String errorMessage = + "Unable to unmarshall response (" + e.getMessage() + "). Response Code: " + + parsedResponse.sdkHttpFullResponse().statusCode() + ", Response Text: " + + parsedResponse.sdkHttpFullResponse().statusText().orElse(null); + throw SdkClientException.builder().message(errorMessage).cause(e).build(); + } + } + + /** + * Responsible for handling an error response, including unmarshalling the error response + * into the most specific exception type possible, and throwing the exception. + */ + private SdkException handleErrorResponse(AwsXmlUnmarshallingContext parsedResponse) { + try { + SdkException exception = errorResponseTransformer.apply(parsedResponse); + exception.fillInStackTrace(); + SdkStandardLogger.REQUEST_LOGGER.debug(() -> "Received error response: " + exception); + return exception; + } catch (Exception e) { + String errorMessage = String.format("Unable to unmarshall error response (%s). " + + "Response Code: %d, Response Text: %s", e.getMessage(), + parsedResponse.sdkHttpFullResponse().statusCode(), + parsedResponse.sdkHttpFullResponse().statusText().orElse("null")); + throw SdkClientException.builder().message(errorMessage).cause(e).build(); + } + } + + /** + * Close the input stream if required. + */ + private void closeInputStreamIfNeeded(SdkHttpFullResponse httpResponse, + boolean didRequestFail) { + // Always close on failed requests. Close on successful requests unless it needs connection left open + if (didRequestFail || !needsConnectionLeftOpen) { + Optional.ofNullable(httpResponse) + .flatMap(SdkHttpFullResponse::content) // If no content, no need to close + .ifPresent(s -> IoUtils.closeQuietly(s, log)); + } + } +} diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseHandler.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseHandler.java index 39590e068a59..71a8b7ae57bd 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseHandler.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseTransformer.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseTransformer.java new file mode 100644 index 000000000000..3a2affb06db3 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlResponseTransformer.java @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import static software.amazon.awssdk.awscore.util.AwsHeader.AWS_REQUEST_ID; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.awscore.AwsResponse; +import software.amazon.awssdk.awscore.AwsResponseMetadata; +import software.amazon.awssdk.awscore.DefaultAwsResponseMetadata; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.core.SdkStandardLogger; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; + +/** + * A transformer function that takes a parsed XML response and converts it into an {@link AwsResponse}. Used + * as a component in the {@link AwsXmlPredicatedResponseHandler}. + */ +@SdkInternalApi +public final class AwsXmlResponseTransformer + implements Function { + + private static final String X_AMZN_REQUEST_ID_HEADER = "x-amzn-RequestId"; + + private final XmlProtocolUnmarshaller unmarshaller; + private final Function pojoSupplier; + + public AwsXmlResponseTransformer(XmlProtocolUnmarshaller unmarshaller, + Function pojoSupplier) { + this.unmarshaller = unmarshaller; + this.pojoSupplier = pojoSupplier; + } + + @Override + public T apply(AwsXmlUnmarshallingContext context) { + return unmarshallResponse(context.sdkHttpFullResponse(), context.parsedRootXml()); + } + + @SuppressWarnings("unchecked") + private T unmarshallResponse(SdkHttpFullResponse response, XmlElement parsedXml) { + SdkStandardLogger.REQUEST_LOGGER.trace(() -> "Unmarshalling parsed service response XML."); + T result = unmarshaller.unmarshall(pojoSupplier.apply(response), parsedXml, response); + SdkStandardLogger.REQUEST_LOGGER.trace(() -> "Done unmarshalling parsed service response."); + AwsResponseMetadata responseMetadata = generateResponseMetadata(response); + return (T) result.toBuilder().responseMetadata(responseMetadata).build(); + } + + /** + * Create the default {@link AwsResponseMetadata}. This might be wrapped by a service + * specific metadata object to provide modeled access to additional metadata. (See S3 and Kinesis). + */ + private AwsResponseMetadata generateResponseMetadata(SdkHttpResponse response) { + Map metadata = new HashMap<>(); + metadata.put(AWS_REQUEST_ID, + response.firstMatchingHeader(X_AMZN_REQUEST_ID_HEADER).orElse(null)); + + response.headers().forEach((key, value) -> metadata.put(key, value.get(0))); + return DefaultAwsResponseMetadata.create(metadata); + } +} diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlUnmarshallingContext.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlUnmarshallingContext.java new file mode 100644 index 000000000000..730fca7daa88 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlUnmarshallingContext.java @@ -0,0 +1,168 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; + +/** + * A data class to hold all the context of an unmarshalling stage for the AWS XML protocol as orchestrated by + * {@link AwsXmlPredicatedResponseHandler}. + */ +@SdkInternalApi +public class AwsXmlUnmarshallingContext { + private final SdkHttpFullResponse sdkHttpFullResponse; + private final XmlElement parsedXml; + private final ExecutionAttributes executionAttributes; + private final Boolean isResponseSuccess; + private final XmlElement parsedErrorXml; + + private AwsXmlUnmarshallingContext(Builder builder) { + this.sdkHttpFullResponse = builder.sdkHttpFullResponse; + this.parsedXml = builder.parsedXml; + this.executionAttributes = builder.executionAttributes; + this.isResponseSuccess = builder.isResponseSuccess; + this.parsedErrorXml = builder.parsedErrorXml; + } + + public static Builder builder() { + return new Builder(); + } + + /** + * The HTTP response. + */ + public SdkHttpFullResponse sdkHttpFullResponse() { + return sdkHttpFullResponse; + } + + /** + * The parsed XML of the body, or null if there was no body. + */ + public XmlElement parsedRootXml() { + return parsedXml; + } + + /** + * The {@link ExecutionAttributes} associated with this request. + */ + public ExecutionAttributes executionAttributes() { + return executionAttributes; + } + + /** + * true if the response indicates success; false if not; null if that has not been determined yet + */ + public Boolean isResponseSuccess() { + return isResponseSuccess; + } + + /** + * The parsed XML of just the error. null if not found or determined yet. + */ + public XmlElement parsedErrorXml() { + return parsedErrorXml; + } + + public Builder toBuilder() { + return builder().sdkHttpFullResponse(this.sdkHttpFullResponse) + .parsedXml(this.parsedXml) + .executionAttributes(this.executionAttributes) + .isResponseSuccess(this.isResponseSuccess) + .parsedErrorXml(this.parsedErrorXml); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + AwsXmlUnmarshallingContext that = (AwsXmlUnmarshallingContext) o; + + if (sdkHttpFullResponse != null ? ! sdkHttpFullResponse.equals(that.sdkHttpFullResponse) : + that.sdkHttpFullResponse != null) { + return false; + } + if (parsedXml != null ? ! parsedXml.equals(that.parsedXml) : that.parsedXml != null) { + return false; + } + if (executionAttributes != null ? ! executionAttributes.equals(that.executionAttributes) : + that.executionAttributes != null) { + return false; + } + if (isResponseSuccess != null ? ! isResponseSuccess.equals(that.isResponseSuccess) : + that.isResponseSuccess != null) { + return false; + } + return parsedErrorXml != null ? parsedErrorXml.equals(that.parsedErrorXml) : that.parsedErrorXml == null; + } + + @Override + public int hashCode() { + int result = sdkHttpFullResponse != null ? sdkHttpFullResponse.hashCode() : 0; + result = 31 * result + (parsedXml != null ? parsedXml.hashCode() : 0); + result = 31 * result + (executionAttributes != null ? executionAttributes.hashCode() : 0); + result = 31 * result + (isResponseSuccess != null ? isResponseSuccess.hashCode() : 0); + result = 31 * result + (parsedErrorXml != null ? parsedErrorXml.hashCode() : 0); + return result; + } + + public static final class Builder { + private SdkHttpFullResponse sdkHttpFullResponse; + private XmlElement parsedXml; + private ExecutionAttributes executionAttributes; + private Boolean isResponseSuccess; + private XmlElement parsedErrorXml; + + private Builder() { + } + + public Builder sdkHttpFullResponse(SdkHttpFullResponse sdkHttpFullResponse) { + this.sdkHttpFullResponse = sdkHttpFullResponse; + return this; + } + + public Builder parsedXml(XmlElement parsedXml) { + this.parsedXml = parsedXml; + return this; + } + + public Builder executionAttributes(ExecutionAttributes executionAttributes) { + this.executionAttributes = executionAttributes; + return this; + } + + public Builder isResponseSuccess(Boolean isResponseSuccess) { + this.isResponseSuccess = isResponseSuccess; + return this; + } + + public Builder parsedErrorXml(XmlElement parsedErrorXml) { + this.parsedErrorXml = parsedErrorXml; + return this; + } + + public AwsXmlUnmarshallingContext build() { + return new AwsXmlUnmarshallingContext(this); + } + } +} diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshaller.java new file mode 100644 index 000000000000..2d31826adefa --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshaller.java @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import java.util.Optional; +import java.util.function.Function; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; + +/** + * A function that decorates a {@link AwsXmlUnmarshallingContext} that already contains the parsed XML of the + * response body with parsed error XML if the HTTP response status indicates failure or a serialized error is found + * in the XML body of a 'successful' response. This is a non-standard error handling behavior that is used by some + * non-streaming S3 operations. + */ +@SdkInternalApi +public class DecorateErrorFromResponseBodyUnmarshaller + implements Function { + + private static final String ERROR_IN_SUCCESS_BODY_ELEMENT_NAME = "Error"; + + private final Function> errorRootLocationFunction; + + private DecorateErrorFromResponseBodyUnmarshaller(Function> errorRootLocationFunction) { + this.errorRootLocationFunction = errorRootLocationFunction; + } + + /** + * Constructs a function that can be used to decorate a parsed error from a response if one is found. + * @param errorRootFunction A function that can be used to locate the root of the serialized error in the XML + * body if the HTTP status code of the response indicates an error. This function is not + * applied for HTTP responses that indicate success, instead the root of the document + * will always be checked for an element tagged 'Error'. + * @return An unmarshalling function that will decorate the unmarshalling context with a parsed error if one is + * found in the response. + */ + public static DecorateErrorFromResponseBodyUnmarshaller of(Function> errorRootFunction) { + return new DecorateErrorFromResponseBodyUnmarshaller(errorRootFunction); + } + + @Override + public AwsXmlUnmarshallingContext apply(AwsXmlUnmarshallingContext context) { + Optional parsedRootXml = Optional.ofNullable(context.parsedRootXml()); + + if (!context.sdkHttpFullResponse().isSuccessful()) { + // Request was non-2xx, defer to protocol handler for error root + Optional parsedErrorXml = parsedRootXml.flatMap(errorRootLocationFunction); + return context.toBuilder().isResponseSuccess(false).parsedErrorXml(parsedErrorXml.orElse(null)).build(); + } + + // Check body to see if an error turned up there + Optional parsedErrorXml = parsedRootXml.isPresent() ? + getErrorRootFromSuccessBody(context.parsedRootXml()) : Optional.empty(); + + // Request had an HTTP success code, but an error was found in the body + return parsedErrorXml.map(xmlElement -> context.toBuilder() + .isResponseSuccess(false) + .parsedErrorXml(xmlElement) + .build()) + // Otherwise the response can be considered successful + .orElseGet(() -> context.toBuilder() + .isResponseSuccess(true) + .build()); + } + + private static Optional getErrorRootFromSuccessBody(XmlElement document) { + return ERROR_IN_SUCCESS_BODY_ELEMENT_NAME.equals(document.elementName()) ? + Optional.of(document) : Optional.empty(); + } +} diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/HeaderUnmarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/HeaderUnmarshaller.java index 10f51623fbf4..ac23dcad2220 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/HeaderUnmarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/HeaderUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlPayloadUnmarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlPayloadUnmarshaller.java index 4abda25bd29f..c26a77e5f773 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlPayloadUnmarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlPayloadUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlProtocolUnmarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlProtocolUnmarshaller.java index de7141fa3c3e..3912f75f57b2 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlProtocolUnmarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlProtocolUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -29,10 +29,10 @@ import software.amazon.awssdk.core.protocol.MarshallingType; import software.amazon.awssdk.core.traits.PayloadTrait; import software.amazon.awssdk.core.traits.TimestampFormatTrait; +import software.amazon.awssdk.core.traits.XmlAttributeTrait; import software.amazon.awssdk.http.SdkHttpFullResponse; import software.amazon.awssdk.protocols.core.StringToInstant; import software.amazon.awssdk.protocols.core.StringToValueConverter; -import software.amazon.awssdk.protocols.query.unmarshall.XmlDomParser; import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; import software.amazon.awssdk.protocols.query.unmarshall.XmlErrorUnmarshaller; import software.amazon.awssdk.utils.CollectionUtils; @@ -49,12 +49,14 @@ public final class XmlProtocolUnmarshaller implements XmlErrorUnmarshaller { private XmlProtocolUnmarshaller() { } + public static XmlProtocolUnmarshaller create() { + return new XmlProtocolUnmarshaller(); + } + public TypeT unmarshall(SdkPojo sdkPojo, SdkHttpFullResponse response) { - XmlElement document = hasPayloadMembers(sdkPojo) && response.content().isPresent() - ? XmlDomParser.parse(response.content().get()) : null; - + XmlElement document = XmlResponseParserUtils.parse(sdkPojo, response); return unmarshall(sdkPojo, document, response); } @@ -79,30 +81,38 @@ SdkPojo unmarshall(XmlUnmarshallerContext context, SdkPojo sdkPojo, XmlElement r XmlUnmarshaller unmarshaller = REGISTRY.getUnmarshaller(field.location(), field.marshallingType()); if (root != null && field.location() == MarshallLocation.PAYLOAD) { - List element = isExplicitPayloadMember(field) ? - singletonList(root) : - root.getElementsByName(field.unmarshallLocationName()); - if (!CollectionUtils.isNullOrEmpty(element)) { - Object unmarshalled = unmarshaller.unmarshall(context, element, (SdkField) field); - field.set(sdkPojo, unmarshalled); + if (isAttribute(field)) { + root.getOptionalAttributeByName(field.unmarshallLocationName()) + .ifPresent(e -> field.set(sdkPojo, e)); + } else { + List element = isExplicitPayloadMember(field) ? + singletonList(root) : + root.getElementsByName(field.unmarshallLocationName()); + + if (!CollectionUtils.isNullOrEmpty(element)) { + Object unmarshalled = unmarshaller.unmarshall(context, element, (SdkField) field); + field.set(sdkPojo, unmarshalled); + } } } else { Object unmarshalled = unmarshaller.unmarshall(context, null, (SdkField) field); field.set(sdkPojo, unmarshalled); } } + + if (!(sdkPojo instanceof Buildable)) { + throw new RuntimeException("The sdkPojo passed to the unmarshaller is not buildable (must implement " + + "Buildable)"); + } return (SdkPojo) ((Buildable) sdkPojo).build(); } - private boolean isExplicitPayloadMember(SdkField field) { - return field.containsTrait(PayloadTrait.class); + private boolean isAttribute(SdkField field) { + return field.containsTrait(XmlAttributeTrait.class); } - private boolean hasPayloadMembers(SdkPojo sdkPojo) { - return sdkPojo.sdkFields().stream() - .filter(f -> f.location() == MarshallLocation.PAYLOAD) - .findAny() - .isPresent(); + private boolean isExplicitPayloadMember(SdkField field) { + return field.containsTrait(PayloadTrait.class); } private static Map getDefaultTimestampFormats() { @@ -139,27 +149,4 @@ private static XmlUnmarshallerRegistry createUnmarshallerRegistry() { .payloadUnmarshaller(MarshallingType.MAP, XmlPayloadUnmarshaller::unmarshallMap) .build(); } - - /** - * @return New {@link Builder} instance. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for {@link XmlProtocolUnmarshaller}. - */ - public static final class Builder { - - private Builder() { - } - - /** - * @return New instance of {@link XmlProtocolUnmarshaller}. - */ - public XmlProtocolUnmarshaller build() { - return new XmlProtocolUnmarshaller(); - } - } } diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlResponseParserUtils.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlResponseParserUtils.java new file mode 100644 index 000000000000..55836b80f1f9 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlResponseParserUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.SdkPojo; +import software.amazon.awssdk.core.protocol.MarshallLocation; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.protocols.query.unmarshall.XmlDomParser; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; + +/** + * Static methods to assist with parsing the response of AWS XML requests. + */ +@SdkInternalApi +public final class XmlResponseParserUtils { + private XmlResponseParserUtils() { + } + + /** + * Parse an XML response if one is expected and available. If we are not expecting a payload, but the HTTP response + * code shows an error then we will parse it anyway, as it should contain a serialized error. + * @param sdkPojo the SDK builder object associated with the final response + * @param response the HTTP response + * @return A parsed XML document or an empty XML document if no payload/contents were found in the response. + */ + public static XmlElement parse(SdkPojo sdkPojo, SdkHttpFullResponse response) { + + try { + Optional responseContent = response.content(); + + // In some cases the responseContent is present but empty, so when we are not expecting a body we should + // not attempt to parse it even if the body appears to be present. + if ((!response.isSuccessful() || hasPayloadMembers(sdkPojo)) && responseContent.isPresent()) { + return XmlDomParser.parse(responseContent.get()); + } else { + return XmlElement.empty(); + } + } catch (RuntimeException e) { + if (response.isSuccessful()) { + throw e; + } + + return XmlElement.empty(); + } + } + + private static boolean hasPayloadMembers(SdkPojo sdkPojo) { + return sdkPojo.sdkFields().stream() + .anyMatch(f -> f.location() == MarshallLocation.PAYLOAD); + } +} diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshaller.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshaller.java index 2aa1e01c0585..35f4ac37c1a3 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshaller.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerContext.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerContext.java index cb453781767e..4bf9d4d9ce30 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerContext.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerRegistry.java b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerRegistry.java index 043bcd9b700c..9b56d67bc4c0 100644 --- a/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerRegistry.java +++ b/core/protocols/aws-xml-protocol/src/main/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/XmlUnmarshallerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/aws-xml-protocol/src/test/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlUnmarshallingContextTest.java b/core/protocols/aws-xml-protocol/src/test/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlUnmarshallingContextTest.java new file mode 100644 index 000000000000..207ec55788d8 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/test/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/AwsXmlUnmarshallingContextTest.java @@ -0,0 +1,134 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.internal.InternalCoreExecutionAttribute; +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; + +@RunWith(MockitoJUnitRunner.class) +public class AwsXmlUnmarshallingContextTest { + private static final XmlElement XML_ELEMENT_1 = XmlElement.builder().elementName("one").build(); + private static final XmlElement XML_ELEMENT_2 = XmlElement.builder().elementName("two").build(); + private static final XmlElement XML_ERROR_ELEMENT_1 = XmlElement.builder().elementName("error-one").build(); + private static final XmlElement XML_ERROR_ELEMENT_2 = XmlElement.builder().elementName("error-two").build(); + private static final ExecutionAttributes EXECUTION_ATTRIBUTES_1 = + new ExecutionAttributes().putAttribute(InternalCoreExecutionAttribute.EXECUTION_ATTEMPT, 1); + private static final ExecutionAttributes EXECUTION_ATTRIBUTES_2 = + new ExecutionAttributes().putAttribute(InternalCoreExecutionAttribute.EXECUTION_ATTEMPT, 2); + + @Mock + private SdkHttpFullResponse mockSdkHttpFullResponse; + + private AwsXmlUnmarshallingContext minimal() { + return AwsXmlUnmarshallingContext.builder().build(); + } + + private AwsXmlUnmarshallingContext maximal() { + return AwsXmlUnmarshallingContext.builder() + .parsedXml(XML_ELEMENT_1) + .parsedErrorXml(XML_ERROR_ELEMENT_1) + .isResponseSuccess(true) + .sdkHttpFullResponse(mockSdkHttpFullResponse) + .executionAttributes(EXECUTION_ATTRIBUTES_1) + .build(); + } + + @Test + public void builder_minimal() { + AwsXmlUnmarshallingContext result = minimal(); + + assertThat(result.isResponseSuccess()).isNull(); + assertThat(result.sdkHttpFullResponse()).isNull(); + assertThat(result.parsedRootXml()).isNull(); + assertThat(result.executionAttributes()).isNull(); + assertThat(result.parsedErrorXml()).isNull(); + } + + @Test + public void builder_maximal() { + AwsXmlUnmarshallingContext result = maximal(); + + assertThat(result.isResponseSuccess()).isTrue(); + assertThat(result.sdkHttpFullResponse()).isEqualTo(mockSdkHttpFullResponse); + assertThat(result.parsedRootXml()).isEqualTo(XML_ELEMENT_1); + assertThat(result.executionAttributes()).isEqualTo(EXECUTION_ATTRIBUTES_1); + assertThat(result.parsedErrorXml()).isEqualTo(XML_ERROR_ELEMENT_1); + } + + @Test + public void toBuilder_maximal() { + assertThat(maximal().toBuilder().build()).isEqualTo(maximal()); + } + + @Test + public void toBuilder_minimal() { + assertThat(minimal().toBuilder().build()).isEqualTo(minimal()); + } + + @Test + public void equals_maximal_positive() { + assertThat(maximal()).isEqualTo(maximal()); + } + + @Test + public void equals_minimal() { + assertThat(minimal()).isEqualTo(minimal()); + } + + @Test + public void equals_maximal_negative() { + assertThat(maximal().toBuilder().isResponseSuccess(false).build()).isNotEqualTo(maximal()); + assertThat(maximal().toBuilder().sdkHttpFullResponse(mock(SdkHttpFullResponse.class)).build()).isNotEqualTo(maximal()); + assertThat(maximal().toBuilder().parsedXml(XML_ELEMENT_2).build()).isNotEqualTo(maximal()); + assertThat(maximal().toBuilder().parsedErrorXml(XML_ERROR_ELEMENT_2).build()).isNotEqualTo(maximal()); + assertThat(maximal().toBuilder().executionAttributes(EXECUTION_ATTRIBUTES_2).build()).isNotEqualTo(maximal()); + } + + @Test + public void hashcode_maximal_positive() { + assertThat(maximal().hashCode()).isEqualTo(maximal().hashCode()); + } + + @Test + public void hashcode_minimal_positive() { + assertThat(minimal().hashCode()).isEqualTo(minimal().hashCode()); + } + + @Test + public void hashcode_maximal_negative() { + assertThat(maximal().toBuilder().isResponseSuccess(false).build().hashCode()) + .isNotEqualTo(maximal().hashCode()); + assertThat(maximal().toBuilder().sdkHttpFullResponse(mock(SdkHttpFullResponse.class)).build().hashCode()) + .isNotEqualTo(maximal().hashCode()); + assertThat(maximal().toBuilder().parsedXml(XML_ELEMENT_2).build().hashCode()) + .isNotEqualTo(maximal().hashCode()); + assertThat(maximal().toBuilder().parsedErrorXml(XML_ERROR_ELEMENT_2).build().hashCode()) + .isNotEqualTo(maximal().hashCode()); + assertThat(maximal().toBuilder().executionAttributes(EXECUTION_ATTRIBUTES_2).build().hashCode()) + .isNotEqualTo(maximal().hashCode()); + } + +} \ No newline at end of file diff --git a/core/protocols/aws-xml-protocol/src/test/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshallerTest.java b/core/protocols/aws-xml-protocol/src/test/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshallerTest.java new file mode 100644 index 000000000000..e69c1e585e52 --- /dev/null +++ b/core/protocols/aws-xml-protocol/src/test/java/software/amazon/awssdk/protocols/xml/internal/unmarshall/DecorateErrorFromResponseBodyUnmarshallerTest.java @@ -0,0 +1,175 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.xml.internal.unmarshall; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; +import java.util.function.Function; + +import org.junit.Test; + +import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.protocols.query.unmarshall.XmlElement; + +public class DecorateErrorFromResponseBodyUnmarshallerTest { + private static final Function> FAIL_TEST_ERROR_ROOT_LOCATOR = + ignored -> { throw new RuntimeException("This function should not have been called"); }; + + @Test + public void status200_noBody() { + DecorateErrorFromResponseBodyUnmarshaller decorateErrorFromResponseBodyUnmarshaller = + DecorateErrorFromResponseBodyUnmarshaller.of(FAIL_TEST_ERROR_ROOT_LOCATOR); + + SdkHttpFullResponse sdkHttpFullResponse = SdkHttpFullResponse.builder() + .statusCode(200) + .build(); + + AwsXmlUnmarshallingContext context = AwsXmlUnmarshallingContext.builder() + .sdkHttpFullResponse(sdkHttpFullResponse) + .build(); + + AwsXmlUnmarshallingContext result = decorateErrorFromResponseBodyUnmarshaller.apply(context); + + assertThat(result.isResponseSuccess()).isTrue(); + assertThat(result.parsedErrorXml()).isNull(); + } + + @Test + public void status200_bodyWithNoError() { + DecorateErrorFromResponseBodyUnmarshaller decorateErrorFromResponseBodyUnmarshaller = + DecorateErrorFromResponseBodyUnmarshaller.of(FAIL_TEST_ERROR_ROOT_LOCATOR); + + SdkHttpFullResponse sdkHttpFullResponse = SdkHttpFullResponse.builder() + .statusCode(200) + .build(); + + XmlElement parsedBody = XmlElement.builder() + .elementName("ValidResponse") + .build(); + + AwsXmlUnmarshallingContext context = AwsXmlUnmarshallingContext.builder() + .sdkHttpFullResponse(sdkHttpFullResponse) + .parsedXml(parsedBody) + .build(); + + AwsXmlUnmarshallingContext result = decorateErrorFromResponseBodyUnmarshaller.apply(context); + + assertThat(result.isResponseSuccess()).isTrue(); + assertThat(result.parsedErrorXml()).isNull(); + } + + @Test + public void status200_bodyWithError() { + DecorateErrorFromResponseBodyUnmarshaller decorateErrorFromResponseBodyUnmarshaller = + DecorateErrorFromResponseBodyUnmarshaller.of(FAIL_TEST_ERROR_ROOT_LOCATOR); + + SdkHttpFullResponse sdkHttpFullResponse = SdkHttpFullResponse.builder() + .statusCode(200) + .build(); + + XmlElement parsedError = XmlElement.builder() + .elementName("test-error") + .build(); + + XmlElement parsedBody = XmlElement.builder() + .elementName("Error") + .addChildElement(parsedError) + .build(); + + AwsXmlUnmarshallingContext context = AwsXmlUnmarshallingContext.builder() + .sdkHttpFullResponse(sdkHttpFullResponse) + .parsedXml(parsedBody) + .build(); + + AwsXmlUnmarshallingContext result = decorateErrorFromResponseBodyUnmarshaller.apply(context); + + assertThat(result.isResponseSuccess()).isFalse(); + assertThat(result.parsedErrorXml()).isSameAs(parsedBody); + } + + @Test + public void status500_noBody() { + DecorateErrorFromResponseBodyUnmarshaller decorateErrorFromResponseBodyUnmarshaller = + DecorateErrorFromResponseBodyUnmarshaller.of(xml -> xml.getOptionalElementByName("test-error")); + + SdkHttpFullResponse sdkHttpFullResponse = SdkHttpFullResponse.builder() + .statusCode(500) + .build(); + + AwsXmlUnmarshallingContext context = AwsXmlUnmarshallingContext.builder() + .sdkHttpFullResponse(sdkHttpFullResponse) + .build(); + + AwsXmlUnmarshallingContext result = decorateErrorFromResponseBodyUnmarshaller.apply(context); + + assertThat(result.isResponseSuccess()).isFalse(); + assertThat(result.parsedErrorXml()).isNull(); + } + + @Test + public void status500_bodyWithNoError() { + DecorateErrorFromResponseBodyUnmarshaller decorateErrorFromResponseBodyUnmarshaller = + DecorateErrorFromResponseBodyUnmarshaller.of(xml -> xml.getOptionalElementByName("test-error")); + + SdkHttpFullResponse sdkHttpFullResponse = SdkHttpFullResponse.builder() + .statusCode(500) + .build(); + + XmlElement parsedBody = XmlElement.builder() + .elementName("ValidResponse") + .build(); + + AwsXmlUnmarshallingContext context = AwsXmlUnmarshallingContext.builder() + .sdkHttpFullResponse(sdkHttpFullResponse) + .parsedXml(parsedBody) + .build(); + + AwsXmlUnmarshallingContext result = decorateErrorFromResponseBodyUnmarshaller.apply(context); + + assertThat(result.isResponseSuccess()).isFalse(); + assertThat(result.parsedErrorXml()).isNull(); + } + + @Test + public void status500_bodyWithError() { + DecorateErrorFromResponseBodyUnmarshaller decorateErrorFromResponseBodyUnmarshaller = + DecorateErrorFromResponseBodyUnmarshaller.of(xml -> xml.getOptionalElementByName("test-error")); + + SdkHttpFullResponse sdkHttpFullResponse = SdkHttpFullResponse.builder() + .statusCode(500) + .build(); + + XmlElement parsedError = XmlElement.builder() + .elementName("test-error") + .build(); + + XmlElement parsedBody = XmlElement.builder() + .elementName("Error") + .addChildElement(parsedError) + .build(); + + AwsXmlUnmarshallingContext context = AwsXmlUnmarshallingContext.builder() + .sdkHttpFullResponse(sdkHttpFullResponse) + .parsedXml(parsedBody) + .build(); + + AwsXmlUnmarshallingContext result = decorateErrorFromResponseBodyUnmarshaller.apply(context); + + assertThat(result.isResponseSuccess()).isFalse(); + assertThat(result.parsedErrorXml()).isSameAs(parsedError); + } +} \ No newline at end of file diff --git a/core/protocols/pom.xml b/core/protocols/pom.xml index d959eb6ca21f..32aef73a8d12 100644 --- a/core/protocols/pom.xml +++ b/core/protocols/pom.xml @@ -1,11 +1,26 @@ + + core software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/protocols/protocol-core/pom.xml b/core/protocols/protocol-core/pom.xml index e2dc80cb5850..8f5ab21658d2 100644 --- a/core/protocols/protocol-core/pom.xml +++ b/core/protocols/protocol-core/pom.xml @@ -1,11 +1,26 @@ + + protocols software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT 4.0.0 diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/AbstractMarshallingRegistry.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/AbstractMarshallingRegistry.java index e39367f57efc..6bd09d52a257 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/AbstractMarshallingRegistry.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/AbstractMarshallingRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ExceptionMetadata.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ExceptionMetadata.java index 7a3dab6aaa21..de298bc1ee4a 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ExceptionMetadata.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ExceptionMetadata.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/InstantToString.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/InstantToString.java index ca71665eab60..96eaa4164932 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/InstantToString.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/InstantToString.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/Marshaller.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/Marshaller.java index 11008876292b..a289dc7d2654 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/Marshaller.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/Marshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationInfo.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationInfo.java index 808894bc6d27..8b4dbf287e49 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationInfo.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationMetadataAttribute.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationMetadataAttribute.java index 76fbe13e9bde..f77f084339b3 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationMetadataAttribute.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/OperationMetadataAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/PathMarshaller.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/PathMarshaller.java index 3a1ce5e52c5b..ff98704e8c6b 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/PathMarshaller.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/PathMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolMarshaller.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolMarshaller.java index 66333fdfc220..7a769d47363c 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolMarshaller.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolMarshaller.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolUtils.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolUtils.java index d28a7251916f..cead56f1cf65 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolUtils.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ProtocolUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToInstant.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToInstant.java index fa41b4f7f3fc..a9888f076c1d 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToInstant.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToInstant.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToValueConverter.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToValueConverter.java index d38e8757f049..e1fe1d2ec73e 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToValueConverter.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/StringToValueConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ValueToStringConverter.java b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ValueToStringConverter.java index ce2192e7f206..8dc35eb35bdd 100644 --- a/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ValueToStringConverter.java +++ b/core/protocols/protocol-core/src/main/java/software/amazon/awssdk/protocols/core/ValueToStringConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/GreedyPathMarshallerTest.java b/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/GreedyPathMarshallerTest.java index f74cb995c8f2..f53932516a5d 100644 --- a/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/GreedyPathMarshallerTest.java +++ b/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/GreedyPathMarshallerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/NonGreedyPathMarshallerTest.java b/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/NonGreedyPathMarshallerTest.java index c8de721e923c..0994009bb65d 100644 --- a/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/NonGreedyPathMarshallerTest.java +++ b/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/NonGreedyPathMarshallerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/ProtocolUtilsTest.java b/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/ProtocolUtilsTest.java index a29eeef08b78..ffb7c9048f4e 100644 --- a/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/ProtocolUtilsTest.java +++ b/core/protocols/protocol-core/src/test/java/software/amazon/awssdk/protocols/core/ProtocolUtilsTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.protocols.core; import static java.util.Collections.singletonList; diff --git a/core/regions/pom.xml b/core/regions/pom.xml index 974603742bfe..a3e46e116a6b 100644 --- a/core/regions/pom.xml +++ b/core/regions/pom.xml @@ -1,6 +1,6 @@ + + software.amazon.awssdk + http-client-tests + ${awsjavasdk.version} + test + com.github.tomakehurst wiremock @@ -112,6 +118,11 @@ junit test + + org.testng + testng + test + org.mockito mockito-core diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/Http2Configuration.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/Http2Configuration.java new file mode 100644 index 000000000000..93037270e310 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/Http2Configuration.java @@ -0,0 +1,181 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty; + +import java.time.Duration; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * Configuration specific to HTTP/2 connections. + */ +@SdkPublicApi +public final class Http2Configuration implements ToCopyableBuilder { + private final Long maxStreams; + private final Integer initialWindowSize; + private final Duration healthCheckPingPeriod; + + private Http2Configuration(DefaultBuilder builder) { + this.maxStreams = builder.maxStreams; + this.initialWindowSize = builder.initialWindowSize; + this.healthCheckPingPeriod = builder.healthCheckPingPeriod; + } + + /** + * @return The maximum number of streams to be created per HTTP/2 connection. + */ + public Long maxStreams() { + return maxStreams; + } + + /** + * @return The initial window size for an HTTP/2 stream. + */ + public Integer initialWindowSize() { + return initialWindowSize; + } + + /** + * @return The health check period for an HTTP/2 connection. + */ + public Duration healthCheckPingPeriod() { + return healthCheckPingPeriod; + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + Http2Configuration that = (Http2Configuration) o; + + if (maxStreams != null ? !maxStreams.equals(that.maxStreams) : that.maxStreams != null) { + return false; + } + + return initialWindowSize != null ? initialWindowSize.equals(that.initialWindowSize) : that.initialWindowSize == null; + + } + + @Override + public int hashCode() { + int result = maxStreams != null ? maxStreams.hashCode() : 0; + result = 31 * result + (initialWindowSize != null ? initialWindowSize.hashCode() : 0); + return result; + } + + public static Builder builder() { + return new DefaultBuilder(); + } + + public interface Builder extends CopyableBuilder { + + /** + * Sets the max number of concurrent streams per connection. + * + *

    Note that this cannot exceed the value of the MAX_CONCURRENT_STREAMS setting returned by the service. If it + * does the service setting is used instead.

    + * + * @param maxStreams Max concurrent HTTP/2 streams per connection. + * @return This builder for method chaining. + */ + Builder maxStreams(Long maxStreams); + + /** + * Sets initial window size of a stream. This setting is only respected when the HTTP/2 protocol is used. + * + * See https://tools.ietf.org/html/rfc7540#section-6.5.2 + * for more information about this parameter. + * + * @param initialWindowSize The initial window size of a stream. + * @return This builder for method chaining. + */ + Builder initialWindowSize(Integer initialWindowSize); + + /** + * Sets the period that the Netty client will send {@code PING} frames to the remote endpoint to check the + * health of the connection. The default value is {@link + * software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration#HTTP2_CONNECTION_PING_TIMEOUT_SECONDS}. To + * disable this feature, set a duration of 0. + * + * @param healthCheckPingPeriod The ping period. + * @return This builder for method chaining. + */ + Builder healthCheckPingPeriod(Duration healthCheckPingPeriod); + } + + private static final class DefaultBuilder implements Builder { + private Long maxStreams; + private Integer initialWindowSize; + private Duration healthCheckPingPeriod; + + private DefaultBuilder() { + } + + private DefaultBuilder(Http2Configuration http2Configuration) { + this.maxStreams = http2Configuration.maxStreams; + this.initialWindowSize = http2Configuration.initialWindowSize; + this.healthCheckPingPeriod = http2Configuration.healthCheckPingPeriod; + } + + @Override + public Builder maxStreams(Long maxStreams) { + this.maxStreams = Validate.isPositiveOrNull(maxStreams, "maxStreams"); + return this; + } + + public void setMaxStreams(Long maxStreams) { + maxStreams(maxStreams); + } + + @Override + public Builder initialWindowSize(Integer initialWindowSize) { + this.initialWindowSize = Validate.isPositiveOrNull(initialWindowSize, "initialWindowSize"); + return this; + } + + public void setInitialWindowSize(Integer initialWindowSize) { + initialWindowSize(initialWindowSize); + } + + @Override + public Builder healthCheckPingPeriod(Duration healthCheckPingPeriod) { + this.healthCheckPingPeriod = healthCheckPingPeriod; + return this; + } + + public void setHealthCheckPingPeriod(Duration healthCheckPingPeriod) { + healthCheckPingPeriod(healthCheckPingPeriod); + } + + @Override + public Http2Configuration build() { + return new Http2Configuration(this); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java index 751c3d77937e..b6f2bbafb098 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,16 +15,6 @@ package software.amazon.awssdk.http.nio.netty; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_TIMEOUT; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_CONNECTIONS; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_PENDING_CONNECTION_ACQUIRES; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.READ_TIMEOUT; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.REAP_IDLE_CONNECTIONS; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TLS_KEY_MANAGERS_PROVIDER; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.WRITE_TIMEOUT; import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.EVENTLOOP_SHUTDOWN_FUTURE_TIMEOUT_SECONDS; import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.EVENTLOOP_SHUTDOWN_QUIET_PERIOD_SECONDS; import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.EVENTLOOP_SHUTDOWN_TIMEOUT_SECONDS; @@ -42,6 +32,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.annotations.SdkPublicApi; @@ -51,6 +42,7 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SystemPropertyTlsKeyManagersProvider; import software.amazon.awssdk.http.TlsKeyManagersProvider; +import software.amazon.awssdk.http.TlsTrustManagersProvider; import software.amazon.awssdk.http.async.AsyncExecuteRequest; import software.amazon.awssdk.http.async.SdkAsyncHttpClient; import software.amazon.awssdk.http.nio.netty.internal.AwaitCloseChannelPoolMap; @@ -77,6 +69,14 @@ public final class NettyNioAsyncHttpClient implements SdkAsyncHttpClient { private static final Logger log = LoggerFactory.getLogger(NettyNioAsyncHttpClient.class); private static final long MAX_STREAMS_ALLOWED = 4294967295L; // unsigned 32-bit, 2^32 -1 + private static final int DEFAULT_INITIAL_WINDOW_SIZE = 1_048_576; // 1MiB + + // Override connection idle timeout for Netty http client to reduce the frequency of "server failed to complete the + // response error". see https://github.com/aws/aws-sdk-java-v2/issues/1122 + private static final AttributeMap NETTY_HTTP_DEFAULTS = + AttributeMap.builder() + .put(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT, Duration.ofSeconds(5)) + .build(); private final SdkEventLoopGroup sdkEventLoopGroup; private final SdkChannelPoolMap pools; @@ -85,13 +85,20 @@ public final class NettyNioAsyncHttpClient implements SdkAsyncHttpClient { private NettyNioAsyncHttpClient(DefaultBuilder builder, AttributeMap serviceDefaultsMap) { this.configuration = new NettyConfiguration(serviceDefaultsMap); Protocol protocol = serviceDefaultsMap.get(SdkHttpConfigurationOption.PROTOCOL); - long maxStreams = builder.maxHttp2Streams == null ? MAX_STREAMS_ALLOWED : builder.maxHttp2Streams; this.sdkEventLoopGroup = eventLoopGroup(builder); + + Http2Configuration http2Configuration = builder.http2Configuration; + + long maxStreams = resolveMaxHttp2Streams(builder.maxHttp2Streams, http2Configuration); + int initialWindowSize = resolveInitialWindowSize(http2Configuration); + this.pools = AwaitCloseChannelPoolMap.builder() .sdkChannelOptions(builder.sdkChannelOptions) .configuration(configuration) .protocol(protocol) .maxStreams(maxStreams) + .initialWindowSize(initialWindowSize) + .healthCheckPingPeriod(resolveHealthCheckPingPeriod(http2Configuration)) .sdkEventLoopGroup(sdkEventLoopGroup) .sslProvider(resolveSslProvider(builder)) .proxyConfiguration(builder.proxyConfiguration) @@ -143,6 +150,32 @@ private SslProvider resolveSslProvider(DefaultBuilder builder) { return SslContext.defaultClientProvider(); } + private long resolveMaxHttp2Streams(Integer topLevelValue, Http2Configuration http2Configuration) { + if (topLevelValue != null) { + return topLevelValue; + } + + if (http2Configuration == null || http2Configuration.maxStreams() == null) { + return MAX_STREAMS_ALLOWED; + } + + return Math.min(http2Configuration.maxStreams(), MAX_STREAMS_ALLOWED); + } + + private int resolveInitialWindowSize(Http2Configuration http2Configuration) { + if (http2Configuration == null || http2Configuration.initialWindowSize() == null) { + return DEFAULT_INITIAL_WINDOW_SIZE; + } + return http2Configuration.initialWindowSize(); + } + + private Duration resolveHealthCheckPingPeriod(Http2Configuration http2Configuration) { + if (http2Configuration != null) { + return http2Configuration.healthCheckPingPeriod(); + } + return null; + } + private SdkEventLoopGroup nonManagedEventLoopGroup(SdkEventLoopGroup eventLoopGroup) { return SdkEventLoopGroup.create(new NonManagedEventLoopGroup(eventLoopGroup.eventLoopGroup()), eventLoopGroup.channelFactory()); @@ -175,6 +208,11 @@ public String clientName() { return CLIENT_NAME; } + @SdkTestInternalApi + NettyConfiguration configuration() { + return configuration; + } + /** * Builder that allows configuration of the Netty NIO HTTP implementation. Use {@link #builder()} to configure and construct * a Netty HTTP client. @@ -332,6 +370,9 @@ public interface Builder extends SdkAsyncHttpClient.Builder + * Note:If {@link #maxHttp2Streams(Integer)} and {@link Http2Configuration#maxStreams()} are both set, + * the value set using {@link #maxHttp2Streams(Integer)} takes precedence. + * + * @param http2Configuration The HTTP/2 configuration object. + * @return the builder for method chaining. + */ + Builder http2Configuration(Http2Configuration http2Configuration); + + /** + * Set the HTTP/2 specific configuration for this client. + *

    + * Note:If {@link #maxHttp2Streams(Integer)} and {@link Http2Configuration#maxStreams()} are both set, + * the value set using {@link #maxHttp2Streams(Integer)} takes precedence. + * + * @param http2ConfigurationBuilderConsumer The consumer of the HTTP/2 configuration builder object. + * @return the builder for method chaining. + */ + Builder http2Configuration(Consumer http2ConfigurationBuilderConsumer); } /** @@ -383,6 +455,7 @@ private static final class DefaultBuilder implements Builder { private SdkEventLoopGroup eventLoopGroup; private SdkEventLoopGroup.Builder eventLoopGroupBuilder; private Integer maxHttp2Streams; + private Http2Configuration http2Configuration; private SslProvider sslProvider; private ProxyConfiguration proxyConfiguration; @@ -391,7 +464,7 @@ private DefaultBuilder() { @Override public Builder maxConcurrency(Integer maxConcurrency) { - standardOptions.put(MAX_CONNECTIONS, maxConcurrency); + standardOptions.put(SdkHttpConfigurationOption.MAX_CONNECTIONS, maxConcurrency); return this; } @@ -401,7 +474,7 @@ public void setMaxConcurrency(Integer maxConnectionsPerEndpoint) { @Override public Builder maxPendingConnectionAcquires(Integer maxPendingAcquires) { - standardOptions.put(MAX_PENDING_CONNECTION_ACQUIRES, maxPendingAcquires); + standardOptions.put(SdkHttpConfigurationOption.MAX_PENDING_CONNECTION_ACQUIRES, maxPendingAcquires); return this; } @@ -412,7 +485,7 @@ public void setMaxPendingConnectionAcquires(Integer maxPendingAcquires) { @Override public Builder readTimeout(Duration readTimeout) { Validate.isNotNegative(readTimeout, "readTimeout"); - standardOptions.put(READ_TIMEOUT, readTimeout); + standardOptions.put(SdkHttpConfigurationOption.READ_TIMEOUT, readTimeout); return this; } @@ -423,7 +496,7 @@ public void setReadTimeout(Duration readTimeout) { @Override public Builder writeTimeout(Duration writeTimeout) { Validate.isNotNegative(writeTimeout, "writeTimeout"); - standardOptions.put(WRITE_TIMEOUT, writeTimeout); + standardOptions.put(SdkHttpConfigurationOption.WRITE_TIMEOUT, writeTimeout); return this; } @@ -434,7 +507,7 @@ public void setWriteTimeout(Duration writeTimeout) { @Override public Builder connectionTimeout(Duration timeout) { Validate.isPositive(timeout, "connectionTimeout"); - standardOptions.put(CONNECTION_TIMEOUT, timeout); + standardOptions.put(SdkHttpConfigurationOption.CONNECTION_TIMEOUT, timeout); return this; } @@ -445,7 +518,7 @@ public void setConnectionTimeout(Duration connectionTimeout) { @Override public Builder connectionAcquisitionTimeout(Duration connectionAcquisitionTimeout) { Validate.isPositive(connectionAcquisitionTimeout, "connectionAcquisitionTimeout"); - standardOptions.put(CONNECTION_ACQUIRE_TIMEOUT, connectionAcquisitionTimeout); + standardOptions.put(SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT, connectionAcquisitionTimeout); return this; } @@ -456,7 +529,7 @@ public void setConnectionAcquisitionTimeout(Duration connectionAcquisitionTimeou @Override public Builder connectionTimeToLive(Duration connectionTimeToLive) { Validate.isPositive(connectionTimeToLive, "connectionTimeToLive"); - standardOptions.put(CONNECTION_TIME_TO_LIVE, connectionTimeToLive); + standardOptions.put(SdkHttpConfigurationOption.CONNECTION_TIME_TO_LIVE, connectionTimeToLive); return this; } @@ -467,7 +540,7 @@ public void setConnectionTimeToLive(Duration connectionTimeToLive) { @Override public Builder connectionMaxIdleTime(Duration connectionMaxIdleTime) { Validate.isPositive(connectionMaxIdleTime, "connectionMaxIdleTime"); - standardOptions.put(CONNECTION_MAX_IDLE_TIMEOUT, connectionMaxIdleTime); + standardOptions.put(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT, connectionMaxIdleTime); return this; } @@ -477,7 +550,7 @@ public void setConnectionMaxIdleTime(Duration connectionMaxIdleTime) { @Override public Builder useIdleConnectionReaper(Boolean useIdleConnectionReaper) { - standardOptions.put(REAP_IDLE_CONNECTIONS, useIdleConnectionReaper); + standardOptions.put(SdkHttpConfigurationOption.REAP_IDLE_CONNECTIONS, useIdleConnectionReaper); return this; } @@ -553,14 +626,46 @@ public void setProxyConfiguration(ProxyConfiguration proxyConfiguration) { @Override public Builder tlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider) { - this.standardOptions.put(TLS_KEY_MANAGERS_PROVIDER, tlsKeyManagersProvider); + this.standardOptions.put(SdkHttpConfigurationOption.TLS_KEY_MANAGERS_PROVIDER, tlsKeyManagersProvider); + return this; + } + + public void setTlsKeyManagersProvider(TlsKeyManagersProvider tlsKeyManagersProvider) { + tlsKeyManagersProvider(tlsKeyManagersProvider); + } + + @Override + public Builder tlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider) { + standardOptions.put(SdkHttpConfigurationOption.TLS_TRUST_MANAGERS_PROVIDER, tlsTrustManagersProvider); return this; } + public void setTlsTrustManagersProvider(TlsTrustManagersProvider tlsTrustManagersProvider) { + tlsTrustManagersProvider(tlsTrustManagersProvider); + } + + @Override + public Builder http2Configuration(Http2Configuration http2Configuration) { + this.http2Configuration = http2Configuration; + return this; + } + + @Override + public Builder http2Configuration(Consumer http2ConfigurationBuilderConsumer) { + Http2Configuration.Builder builder = Http2Configuration.builder(); + http2ConfigurationBuilderConsumer.accept(builder); + return http2Configuration(builder.build()); + } + + public void setHttp2Configuration(Http2Configuration http2Configuration) { + http2Configuration(http2Configuration); + } + @Override public SdkAsyncHttpClient buildWithDefaults(AttributeMap serviceDefaults) { return new NettyNioAsyncHttpClient(this, standardOptions.build() .merge(serviceDefaults) + .merge(NETTY_HTTP_DEFAULTS) .merge(SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS)); } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettySdkAsyncHttpService.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettySdkAsyncHttpService.java index 82cb745f7c23..3824b34ed024 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettySdkAsyncHttpService.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettySdkAsyncHttpService.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java index dac1aa090236..5ee2ac1cf977 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java index df6bc1fa6db2..870853c4de69 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java index 732ca8c2366b..01a42222c588 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; -import io.netty.channel.ChannelOption; import io.netty.channel.pool.ChannelPool; import io.netty.channel.pool.ChannelPoolHandler; import io.netty.handler.codec.http2.Http2SecurityUtil; @@ -28,10 +27,9 @@ import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SupportedCipherSuiteFilter; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; - -import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; +import java.time.Duration; import java.util.Collection; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -40,6 +38,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLException; @@ -51,6 +50,7 @@ import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; import software.amazon.awssdk.http.nio.netty.internal.http2.HttpOrHttp2ChannelPool; import software.amazon.awssdk.utils.Logger; +import software.amazon.awssdk.utils.Validate; /** * Implementation of {@link SdkChannelPoolMap} that awaits channel pools to be closed upon closing. @@ -74,31 +74,47 @@ public void channelCreated(Channel ch) throws Exception { } }; + // IMPORTANT: If the default bootstrap provider is changed, ensure that the new implementation is compliant with + // DNS resolver testing in BootstrapProviderTest, specifically that no caching of hostname lookups is taking place. + private static final Function DEFAULT_BOOTSTRAP_PROVIDER = + b -> new BootstrapProvider(b.sdkEventLoopGroup, b.configuration, b.sdkChannelOptions); + private final Map shouldProxyForHostCache = new ConcurrentHashMap<>(); - private final SdkChannelOptions sdkChannelOptions; - private final SdkEventLoopGroup sdkEventLoopGroup; private final NettyConfiguration configuration; private final Protocol protocol; private final long maxStreams; + private final Duration healthCheckPingPeriod; + private final int initialWindowSize; private final SslProvider sslProvider; private final ProxyConfiguration proxyConfiguration; + private final BootstrapProvider bootstrapProvider; - private AwaitCloseChannelPoolMap(Builder builder) { - this.sdkChannelOptions = builder.sdkChannelOptions; - this.sdkEventLoopGroup = builder.sdkEventLoopGroup; + private AwaitCloseChannelPoolMap(Builder builder, Function createBootStrapProvider) { this.configuration = builder.configuration; this.protocol = builder.protocol; this.maxStreams = builder.maxStreams; + this.healthCheckPingPeriod = builder.healthCheckPingPeriod; + this.initialWindowSize = builder.initialWindowSize; this.sslProvider = builder.sslProvider; this.proxyConfiguration = builder.proxyConfiguration; + this.bootstrapProvider = createBootStrapProvider.apply(builder); + } + + private AwaitCloseChannelPoolMap(Builder builder) { + this(builder, DEFAULT_BOOTSTRAP_PROVIDER); } @SdkTestInternalApi - AwaitCloseChannelPoolMap(Builder builder, Map shouldProxyForHostCache) { - this(builder); - this.shouldProxyForHostCache.putAll(shouldProxyForHostCache); + AwaitCloseChannelPoolMap(Builder builder, + Map shouldProxyForHostCache, + BootstrapProvider bootstrapProvider) { + this(builder, bootstrapProvider == null ? DEFAULT_BOOTSTRAP_PROVIDER : b -> bootstrapProvider); + + if (shouldProxyForHostCache != null) { + this.shouldProxyForHostCache.putAll(shouldProxyForHostCache); + } } public static Builder builder() { @@ -113,8 +129,15 @@ protected SimpleChannelPoolAwareChannelPool newPool(URI key) { AtomicReference channelPoolRef = new AtomicReference<>(); - ChannelPipelineInitializer pipelineInitializer = - new ChannelPipelineInitializer(protocol, sslContext, maxStreams, channelPoolRef, configuration, key); + ChannelPipelineInitializer pipelineInitializer = new ChannelPipelineInitializer(protocol, + sslContext, + sslProvider, + maxStreams, + initialWindowSize, + healthCheckPingPeriod, + channelPoolRef, + configuration, + key); BetterSimpleChannelPool tcpChannelPool; ChannelPool baseChannelPool; @@ -161,17 +184,7 @@ public void close() { private Bootstrap createBootstrap(URI poolKey) { String host = bootstrapHost(poolKey); int port = bootstrapPort(poolKey); - - Bootstrap bootstrap = - new Bootstrap() - .group(sdkEventLoopGroup.eventLoopGroup()) - .channelFactory(sdkEventLoopGroup.channelFactory()) - .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, configuration.connectTimeoutMillis()) - // TODO run some performance tests with and without this. - .remoteAddress(new InetSocketAddress(host, port)); - sdkChannelOptions.channelOptions().forEach(bootstrap::option); - - return bootstrap; + return bootstrapProvider.createBootstrap(host, port); } @@ -270,7 +283,20 @@ private SslContext sslContext(URI targetAddress) { } private TrustManagerFactory getTrustManager() { - return configuration.trustAllCertificates() ? InsecureTrustManagerFactory.INSTANCE : null; + Validate.isTrue(configuration.tlsTrustManagersProvider() == null || !configuration.trustAllCertificates(), + "A TlsTrustManagerProvider can't be provided if TrustAllCertificates is also set"); + + if (configuration.tlsTrustManagersProvider() != null) { + return StaticTrustManagerFactory.create(configuration.tlsTrustManagersProvider().trustManagers()); + } + + if (configuration.trustAllCertificates()) { + log.warn(() -> "SSL Certificate verification is disabled. This is not a safe setting and should only be " + + "used for testing."); + return InsecureTrustManagerFactory.INSTANCE; + } + + return null; } private KeyManagerFactory getKeyManager() { @@ -290,6 +316,8 @@ public static class Builder { private NettyConfiguration configuration; private Protocol protocol; private long maxStreams; + private int initialWindowSize; + private Duration healthCheckPingPeriod; private SslProvider sslProvider; private ProxyConfiguration proxyConfiguration; @@ -321,6 +349,16 @@ public Builder maxStreams(long maxStreams) { return this; } + public Builder initialWindowSize(int initialWindowSize) { + this.initialWindowSize = initialWindowSize; + return this; + } + + public Builder healthCheckPingPeriod(Duration healthCheckPingPeriod) { + this.healthCheckPingPeriod = healthCheckPingPeriod; + return this; + } + public Builder sslProvider(SslProvider sslProvider) { this.sslProvider = sslProvider; return this; diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BetterSimpleChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BetterSimpleChannelPool.java index 2770b3c1de67..3c1ae77d99ca 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BetterSimpleChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BetterSimpleChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProvider.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProvider.java new file mode 100644 index 000000000000..03b18ccd1acb --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProvider.java @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelOption; +import java.net.InetSocketAddress; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; + +/** + * The primary purpose of this Bootstrap provider is to ensure that all Bootstraps created by it are 'unresolved' + * InetSocketAddress. This is to prevent Netty from caching the resolved address of a host and then re-using it in + * subsequent connection attempts, and instead deferring to the JVM to handle address resolution and caching. + */ +@SdkInternalApi +public class BootstrapProvider { + private final SdkEventLoopGroup sdkEventLoopGroup; + private final NettyConfiguration nettyConfiguration; + private final SdkChannelOptions sdkChannelOptions; + + + BootstrapProvider(SdkEventLoopGroup sdkEventLoopGroup, + NettyConfiguration nettyConfiguration, + SdkChannelOptions sdkChannelOptions) { + this.sdkEventLoopGroup = sdkEventLoopGroup; + this.nettyConfiguration = nettyConfiguration; + this.sdkChannelOptions = sdkChannelOptions; + } + + /** + * Creates a Bootstrap for a specific host and port with an unresolved InetSocketAddress as the remoteAddress. + * @param host The unresolved remote hostname + * @param port The remote port + * @return A newly created Bootstrap using the configuration this provider was initialized with, and having an + * unresolved remote address. + */ + public Bootstrap createBootstrap(String host, int port) { + Bootstrap bootstrap = + new Bootstrap() + .group(sdkEventLoopGroup.eventLoopGroup()) + .channelFactory(sdkEventLoopGroup.channelFactory()) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, nettyConfiguration.connectTimeoutMillis()) + .remoteAddress(InetSocketAddress.createUnresolved(host, port)); + sdkChannelOptions.channelOptions().forEach(bootstrap::option); + + return bootstrap; + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPool.java index 2ed939ef7b51..3a972f74fddf 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelAttributeKey.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelAttributeKey.java index 8fc62b60b5cb..f2feca135ffa 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelAttributeKey.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelAttributeKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,13 +17,15 @@ import io.netty.channel.Channel; import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http2.Http2Connection; import io.netty.util.AttributeKey; import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import org.reactivestreams.Subscriber; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.Protocol; -import software.amazon.awssdk.http.nio.netty.internal.http2.MultiplexedChannelRecord; +import software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool; +import software.amazon.awssdk.http.nio.netty.internal.http2.PingTracker; /** * Keys for attributes attached via {@link io.netty.channel.Channel#attr(AttributeKey)}. @@ -38,10 +40,20 @@ public final class ChannelAttributeKey { "aws.http.nio.netty.async.protocolFuture"); /** - * Reference to {@link MultiplexedChannelRecord} which stores information about leased streams for a multiplexed connection. + * Reference to {@link Http2MultiplexedChannelPool} which stores information about leased streams for a multiplexed + * connection. */ - public static final AttributeKey CHANNEL_POOL_RECORD = AttributeKey.newInstance( - "aws.http.nio.netty.async.channelPoolRecord"); + public static final AttributeKey HTTP2_MULTIPLEXED_CHANNEL_POOL = AttributeKey.newInstance( + "aws.http.nio.netty.async.http2MultiplexedChannelPool"); + + public static final AttributeKey PING_TRACKER = + AttributeKey.newInstance("aws.http.nio.netty.async.h2.pingTracker"); + + public static final AttributeKey HTTP2_CONNECTION = + AttributeKey.newInstance("aws.http.nio.netty.async.http2Connection"); + + public static final AttributeKey HTTP2_INITIAL_WINDOW_SIZE = + AttributeKey.newInstance("aws.http.nio.netty.async.http2InitialWindowSize"); /** * Value of the MAX_CONCURRENT_STREAMS from the server's SETTING frame. @@ -49,6 +61,12 @@ public final class ChannelAttributeKey { public static final AttributeKey MAX_CONCURRENT_STREAMS = AttributeKey.newInstance( "aws.http.nio.netty.async.maxConcurrentStreams"); + /** + * {@link AttributeKey} to keep track of whether we should close the connection after this request + * has completed. + */ + static final AttributeKey KEEP_ALIVE = AttributeKey.newInstance("aws.http.nio.netty.async.keepAlive"); + /** * Attribute key for {@link RequestContext}. */ @@ -73,12 +91,6 @@ public final class ChannelAttributeKey { static final AttributeKey EXECUTION_ID_KEY = AttributeKey.newInstance( "aws.http.nio.netty.async.executionId"); - /** - * {@link AttributeKey} to keep track of whether we should close the connection after this request - * has completed. - */ - static final AttributeKey KEEP_ALIVE = AttributeKey.newInstance("aws.http.nio.netty.async.keepAlive"); - /** * Whether the channel is still in use */ diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializer.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializer.java index 890594b375ad..ba29a9872e83 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializer.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,15 +15,22 @@ package software.amazon.awssdk.http.nio.netty.internal; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_CONNECTION; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_INITIAL_WINDOW_SIZE; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; +import static software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration.HTTP2_CONNECTION_PING_TIMEOUT_SECONDS; +import static software.amazon.awssdk.utils.NumericUtils.saturatedCast; import static software.amazon.awssdk.utils.StringUtils.lowerCase; +import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.pool.AbstractChannelPoolHandler; import io.netty.channel.pool.ChannelPool; import io.netty.handler.codec.http.HttpClientCodec; +import io.netty.handler.codec.http2.Http2FrameCodec; import io.netty.handler.codec.http2.Http2FrameCodecBuilder; import io.netty.handler.codec.http2.Http2FrameLogger; import io.netty.handler.codec.http2.Http2MultiplexHandler; @@ -32,14 +39,17 @@ import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; +import io.netty.handler.ssl.SslProvider; import java.net.URI; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.Protocol; -import software.amazon.awssdk.http.nio.netty.internal.http2.Http2GoAwayFrameHandler; +import software.amazon.awssdk.http.nio.netty.internal.http2.Http2GoAwayEventListener; +import software.amazon.awssdk.http.nio.netty.internal.http2.Http2PingHandler; import software.amazon.awssdk.http.nio.netty.internal.http2.Http2SettingsFrameHandler; /** @@ -49,20 +59,29 @@ public final class ChannelPipelineInitializer extends AbstractChannelPoolHandler { private final Protocol protocol; private final SslContext sslCtx; + private final SslProvider sslProvider; private final long clientMaxStreams; + private final int clientInitialWindowSize; + private final Duration healthCheckPingPeriod; private final AtomicReference channelPoolRef; private final NettyConfiguration configuration; private final URI poolKey; public ChannelPipelineInitializer(Protocol protocol, SslContext sslCtx, + SslProvider sslProvider, long clientMaxStreams, + int clientInitialWindowSize, + Duration healthCheckPingPeriod, AtomicReference channelPoolRef, NettyConfiguration configuration, URI poolKey) { this.protocol = protocol; this.sslCtx = sslCtx; + this.sslProvider = sslProvider; this.clientMaxStreams = clientMaxStreams; + this.clientInitialWindowSize = clientInitialWindowSize; + this.healthCheckPingPeriod = healthCheckPingPeriod; this.channelPoolRef = channelPoolRef; this.configuration = configuration; this.poolKey = poolKey; @@ -81,6 +100,12 @@ public void channelCreated(Channel ch) { pipeline.addLast(sslHandler); pipeline.addLast(SslCloseCompletionEventHandler.getInstance()); + + // Use unpooled allocator to avoid increased heap memory usage from Netty 4.1.43. + // See https://github.com/netty/netty/issues/9768 + if (sslProvider == SslProvider.JDK) { + ch.config().setOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT); + } } if (protocol == Protocol.HTTP2) { @@ -98,7 +123,14 @@ public void channelCreated(Channel ch) { } pipeline.addLast(FutureCancelHandler.getInstance()); - pipeline.addLast(UnusedChannelExceptionHandler.getInstance()); + + // Only add it for h1 channel because it does not apply to + // h2 connection channel. It will be attached + // to stream channels when they are created. + if (protocol == Protocol.HTTP1_1) { + pipeline.addLast(UnusedChannelExceptionHandler.getInstance()); + } + pipeline.addLast(new LoggingHandler(LogLevel.DEBUG)); } @@ -119,16 +151,30 @@ private void configureSslEngine(SSLEngine sslEngine) { private void configureHttp2(Channel ch, ChannelPipeline pipeline) { // Using Http2FrameCodecBuilder and Http2MultiplexHandler based on 4.1.37 release notes // https://netty.io/news/2019/06/28/4-1-37-Final.html - pipeline.addLast(Http2FrameCodecBuilder.forClient() - .headerSensitivityDetector((name, value) -> lowerCase(name.toString()) - .equals("authorization")) - .initialSettings(Http2Settings.defaultSettings().initialWindowSize(1_048_576)) - .frameLogger(new Http2FrameLogger(LogLevel.DEBUG)) - .build()); - + Http2FrameCodec codec = + Http2FrameCodecBuilder.forClient() + .headerSensitivityDetector((name, value) -> lowerCase(name.toString()).equals("authorization")) + .initialSettings(Http2Settings.defaultSettings().initialWindowSize(clientInitialWindowSize)) + .frameLogger(new Http2FrameLogger(LogLevel.DEBUG)) + .build(); + + // Connection listeners have higher priority than handlers, in the eyes of the Http2FrameCodec. The Http2FrameCodec will + // close any connections when a GOAWAY is received, but we'd like to send a "GOAWAY happened" exception instead of just + // closing the connection. Because of this, we use a go-away listener instead of a handler, so that we can send the + // exception before the Http2FrameCodec closes the connection itself. + codec.connection().addListener(new Http2GoAwayEventListener(ch)); + + pipeline.addLast(codec); + ch.attr(HTTP2_CONNECTION).set(codec.connection()); + + ch.attr(HTTP2_INITIAL_WINDOW_SIZE).set(clientInitialWindowSize); pipeline.addLast(new Http2MultiplexHandler(new NoOpChannelInitializer())); pipeline.addLast(new Http2SettingsFrameHandler(ch, clientMaxStreams, channelPoolRef)); - pipeline.addLast(new Http2GoAwayFrameHandler()); + if (healthCheckPingPeriod == null) { + pipeline.addLast(new Http2PingHandler(HTTP2_CONNECTION_PING_TIMEOUT_SECONDS * 1_000)); + } else if (healthCheckPingPeriod.toMillis() > 0) { + pipeline.addLast(new Http2PingHandler(saturatedCast(healthCheckPingPeriod.toMillis()))); + } } private void configureHttp11(Channel ch, ChannelPipeline pipeline) { @@ -141,7 +187,6 @@ private static class NoOpChannelInitializer extends ChannelInitializer protected void initChannel(Channel ch) { } } - } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/DelegatingEventLoopGroup.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/DelegatingEventLoopGroup.java index 314c809d730b..ced76358c740 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/DelegatingEventLoopGroup.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/DelegatingEventLoopGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandler.java index 1e89c3a7cb73..3e122537c438 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelledException.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelledException.java index a10643a48b5b..809c8a2fa940 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelledException.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelledException.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPool.java index 23bcf436530f..1ce19749da49 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.IN_USE; import static software.amazon.awssdk.http.nio.netty.internal.utils.ChannelUtils.removeIfExists; -import com.typesafe.netty.http.HttpStreamsClientHandler; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.pool.ChannelPool; @@ -27,6 +26,8 @@ import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.nio.netty.internal.http2.FlushOnReadHandler; +import software.amazon.awssdk.http.nio.netty.internal.nrs.HttpStreamsClientHandler; /** * Removes any per request {@link ChannelHandler} from the pipeline prior to releasing @@ -79,6 +80,7 @@ private void removePerRequestHandlers(Channel channel) { removeIfExists(channel.pipeline(), HttpStreamsClientHandler.class, LastHttpContentHandler.class, + FlushOnReadHandler.class, ResponseHandler.class, ReadTimeoutHandler.class, WriteTimeoutHandler.class); diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPool.java index 8172f91a5a5b..038fcc5f83da 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPool.java index 0d7169e8a173..6fea4f0ea068 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPool.java index 1c051886090f..b2a416c0cafe 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -26,9 +26,7 @@ import io.netty.util.AttributeKey; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; - import java.net.URI; - import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; import software.amazon.awssdk.utils.Logger; diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/IdleConnectionReaperHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/IdleConnectionReaperHandler.java index 7d9ec1805975..f70fd291740e 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/IdleConnectionReaperHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/IdleConnectionReaperHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandler.java index 91c00a3f5d65..4dd1cf822c0b 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallower.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallower.java index e2c777db8616..60c6ab14a419 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallower.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallower.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyConfiguration.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyConfiguration.java index f1d85976fef7..f8abcf11329f 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyConfiguration.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.SdkHttpConfigurationOption; import software.amazon.awssdk.http.TlsKeyManagersProvider; +import software.amazon.awssdk.http.TlsTrustManagersProvider; import software.amazon.awssdk.utils.AttributeMap; /** @@ -37,6 +38,7 @@ public final class NettyConfiguration { public static final int EVENTLOOP_SHUTDOWN_QUIET_PERIOD_SECONDS = 2; public static final int EVENTLOOP_SHUTDOWN_TIMEOUT_SECONDS = 15; public static final int EVENTLOOP_SHUTDOWN_FUTURE_TIMEOUT_SECONDS = 16; + public static final int HTTP2_CONNECTION_PING_TIMEOUT_SECONDS = 5; private final AttributeMap configuration; @@ -64,10 +66,6 @@ public int maxPendingConnectionAcquires() { return configuration.get(MAX_PENDING_CONNECTION_ACQUIRES); } - public boolean trustAllCertificates() { - return configuration.get(TRUST_ALL_CERTIFICATES); - } - public int readTimeoutMillis() { return saturatedCast(configuration.get(SdkHttpConfigurationOption.READ_TIMEOUT).toMillis()); } @@ -91,4 +89,12 @@ public boolean reapIdleConnections() { public TlsKeyManagersProvider tlsKeyManagersProvider() { return configuration.get(SdkHttpConfigurationOption.TLS_KEY_MANAGERS_PROVIDER); } + + public TlsTrustManagersProvider tlsTrustManagersProvider() { + return configuration.get(SdkHttpConfigurationOption.TLS_TRUST_MANAGERS_PROVIDER); + } + + public boolean trustAllCertificates() { + return configuration.get(TRUST_ALL_CERTIFICATES); + } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutor.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutor.java index c0e83302eaab..64d1c152f3a4 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutor.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ package software.amazon.awssdk.http.nio.netty.internal; -import static software.amazon.awssdk.http.Protocol.HTTP1_1; -import static software.amazon.awssdk.http.Protocol.HTTP2; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.EXECUTE_FUTURE_KEY; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.EXECUTION_ID_KEY; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.IN_USE; @@ -25,8 +23,6 @@ import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.REQUEST_CONTEXT_KEY; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.RESPONSE_COMPLETE_KEY; -import com.typesafe.netty.http.HttpStreamsClientHandler; -import com.typesafe.netty.http.StreamedHttpRequest; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; @@ -57,7 +53,6 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; - import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; @@ -65,19 +60,25 @@ import org.slf4j.LoggerFactory; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.http2.FlushOnReadHandler; +import software.amazon.awssdk.http.nio.netty.internal.http2.Http2StreamExceptionHandler; import software.amazon.awssdk.http.nio.netty.internal.http2.Http2ToHttpInboundAdapter; import software.amazon.awssdk.http.nio.netty.internal.http2.HttpToHttp2OutboundAdapter; +import software.amazon.awssdk.http.nio.netty.internal.nrs.HttpStreamsClientHandler; +import software.amazon.awssdk.http.nio.netty.internal.nrs.StreamedHttpRequest; import software.amazon.awssdk.http.nio.netty.internal.utils.ChannelUtils; @SdkInternalApi public final class NettyRequestExecutor { private static final Logger log = LoggerFactory.getLogger(NettyRequestExecutor.class); - private static final RequestAdapter REQUEST_ADAPTER = new RequestAdapter(); + private static final RequestAdapter REQUEST_ADAPTER_HTTP2 = new RequestAdapter(Protocol.HTTP2); + private static final RequestAdapter REQUEST_ADAPTER_HTTP1_1 = new RequestAdapter(Protocol.HTTP1_1); private static final AtomicLong EXECUTION_COUNTER = new AtomicLong(0L); private final long executionId = EXECUTION_COUNTER.incrementAndGet(); private final RequestContext context; private CompletableFuture executeFuture; private Channel channel; + private RequestAdapter requestAdapter; public NettyRequestExecutor(RequestContext context) { this.context = context; @@ -155,17 +156,28 @@ private void configureChannel() { private boolean tryConfigurePipeline() { Protocol protocol = ChannelAttributeKey.getProtocolNow(channel); ChannelPipeline pipeline = channel.pipeline(); - if (HTTP2.equals(protocol)) { - pipeline.addLast(new Http2ToHttpInboundAdapter()); - pipeline.addLast(new HttpToHttp2OutboundAdapter()); - } else if (!HTTP1_1.equals(protocol)) { - String errorMsg = "Unknown protocol: " + protocol; - closeAndRelease(channel); - handleFailure(() -> errorMsg, new RuntimeException(errorMsg)); - return false; + + switch (protocol) { + case HTTP2: + pipeline.addLast(new Http2ToHttpInboundAdapter()); + pipeline.addLast(new HttpToHttp2OutboundAdapter()); + pipeline.addLast(Http2StreamExceptionHandler.create()); + requestAdapter = REQUEST_ADAPTER_HTTP2; + break; + case HTTP1_1: + requestAdapter = REQUEST_ADAPTER_HTTP1_1; + break; + default: + String errorMsg = "Unknown protocol: " + protocol; + closeAndRelease(channel); + handleFailure(() -> errorMsg, new RuntimeException(errorMsg)); + return false; } pipeline.addLast(LastHttpContentHandler.create()); + if (Protocol.HTTP2.equals(protocol)) { + pipeline.addLast(FlushOnReadHandler.getInstance()); + } pipeline.addLast(new HttpStreamsClientHandler()); pipeline.addLast(ResponseHandler.getInstance()); @@ -183,7 +195,7 @@ private boolean tryConfigurePipeline() { } private void makeRequest() { - HttpRequest request = REQUEST_ADAPTER.adapt(context.executeRequest().request()); + HttpRequest request = requestAdapter.adapt(context.executeRequest().request()); writeRequest(request); } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NonManagedEventLoopGroup.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NonManagedEventLoopGroup.java index ba9ceb7fd8d7..518e8f7bbbce 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NonManagedEventLoopGroup.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/NonManagedEventLoopGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandler.java index 9457396c75eb..1625a8b1132a 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandler.java index caded88dcbd5..1bd9bf970249 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandler.java index ff82a10ff584..433d1c8a3444 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ReleaseOnceChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ReleaseOnceChannelPool.java index 196329a61929..e0fb07427704 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ReleaseOnceChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ReleaseOnceChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapter.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapter.java index 628184d8921a..c4fc63af918f 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapter.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -21,23 +21,82 @@ import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http2.HttpConversionUtil.ExtensionHeaderNames; +import java.util.Collections; +import java.util.List; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.utils.StringUtils; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.http.SdkHttpUtils; @SdkInternalApi public final class RequestAdapter { + private static final String HOST = "Host"; + private static final List IGNORE_HEADERS = Collections.singletonList(HOST); + + private final Protocol protocol; + + public RequestAdapter(Protocol protocol) { + this.protocol = Validate.paramNotNull(protocol, "protocol"); + } + public HttpRequest adapt(SdkHttpRequest sdkRequest) { HttpMethod method = toNettyHttpMethod(sdkRequest.method()); HttpHeaders headers = new DefaultHttpHeaders(); - String uri = sdkRequest.getUri().toString(); + String uri = encodedPathAndQueryParams(sdkRequest); + // All requests start out as HTTP/1.1 objects, even if they will + // ultimately be sent over HTTP2. Conversion to H2 is handled at a + // later stage if necessary; see HttpToHttp2OutboundAdapter. DefaultHttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, method, uri, headers); - sdkRequest.headers().forEach(request.headers()::add); + addHeadersToRequest(request, sdkRequest); return request; } private static HttpMethod toNettyHttpMethod(SdkHttpMethod method) { return HttpMethod.valueOf(method.name()); } + + private static String encodedPathAndQueryParams(SdkHttpRequest sdkRequest) { + String encodedPath = sdkRequest.encodedPath(); + if (StringUtils.isBlank(encodedPath)) { + encodedPath = "/"; + } + + String encodedQueryParams = SdkHttpUtils.encodeAndFlattenQueryParameters(sdkRequest.rawQueryParameters()) + .map(queryParams -> "?" + queryParams) + .orElse(""); + + return encodedPath + encodedQueryParams; + } + + /** + * Configures the headers in the specified Netty HTTP request. + */ + private void addHeadersToRequest(DefaultHttpRequest httpRequest, SdkHttpRequest request) { + httpRequest.headers().add(HOST, getHostHeaderValue(request)); + + String scheme = request.getUri().getScheme(); + if (Protocol.HTTP2 == protocol && !StringUtils.isBlank(scheme)) { + httpRequest.headers().add(ExtensionHeaderNames.SCHEME.text(), scheme); + } + + // Copy over any other headers already in our request + request.headers().entrySet().stream() + /* + * Skip the Host header to avoid sending it twice, which will + * interfere with some signing schemes. + */ + .filter(e -> !IGNORE_HEADERS.contains(e.getKey())) + .forEach(e -> e.getValue().forEach(h -> httpRequest.headers().add(e.getKey(), h))); + } + + private String getHostHeaderValue(SdkHttpRequest request) { + return SdkHttpUtils.isUsingStandardPort(request.protocol(), request.port()) + ? request.host() + : request.host() + ":" + request.port(); + } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestContext.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestContext.java index e97bbe7f1728..356703778939 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestContext.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/RequestContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ResponseHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ResponseHandler.java index 8fa2933230e6..3c66a69253a2 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ResponseHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/ResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -25,8 +25,6 @@ import static software.amazon.awssdk.http.nio.netty.internal.utils.ExceptionHandlingUtils.tryCatch; import static software.amazon.awssdk.http.nio.netty.internal.utils.ExceptionHandlingUtils.tryCatchFinally; -import com.typesafe.netty.http.HttpStreamsClientHandler; -import com.typesafe.netty.http.StreamedHttpResponse; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler.Sharable; @@ -60,6 +58,8 @@ import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; import software.amazon.awssdk.http.nio.netty.internal.http2.Http2ResetSendingSubscription; +import software.amazon.awssdk.http.nio.netty.internal.nrs.HttpStreamsClientHandler; +import software.amazon.awssdk.http.nio.netty.internal.nrs.StreamedHttpResponse; import software.amazon.awssdk.utils.FunctionalUtils.UnsafeRunnable; import software.amazon.awssdk.utils.async.DelegatingSubscription; @@ -382,11 +382,11 @@ private Throwable wrapException(Throwable originalCause) { private void notifyIfResponseNotCompleted(ChannelHandlerContext handlerCtx) { RequestContext requestCtx = handlerCtx.channel().attr(REQUEST_CONTEXT_KEY).get(); - boolean responseCompleted = handlerCtx.channel().attr(RESPONSE_COMPLETE_KEY).get(); - boolean lastHttpContentReceived = handlerCtx.channel().attr(LAST_HTTP_CONTENT_RECEIVED_KEY).get(); + Boolean responseCompleted = handlerCtx.channel().attr(RESPONSE_COMPLETE_KEY).get(); + Boolean lastHttpContentReceived = handlerCtx.channel().attr(LAST_HTTP_CONTENT_RECEIVED_KEY).get(); handlerCtx.channel().attr(KEEP_ALIVE).set(false); - if (!responseCompleted && !lastHttpContentReceived) { + if (!Boolean.TRUE.equals(responseCompleted) && !Boolean.TRUE.equals(lastHttpContentReceived)) { IOException err = new IOException("Server failed to send complete response"); runAndLogError("Fail to execute SdkAsyncHttpResponseHandler#onError", () -> requestCtx.handler().onError(err)); executeFuture(handlerCtx).completeExceptionally(err); diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptions.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptions.java index 9cca922b62bb..5c8f21e19e1b 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptions.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelPoolMap.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelPoolMap.java index 102ccc11c685..9d1c0163044e 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelPoolMap.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelPoolMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroup.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroup.java index 9259f418f183..f316b77f3d8b 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroup.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroup.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SimpleChannelPoolAwareChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SimpleChannelPoolAwareChannelPool.java index c8a231b07258..dd9778521321 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SimpleChannelPoolAwareChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SimpleChannelPoolAwareChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandler.java index ce59eea73565..2a7f861fb661 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactory.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactory.java index b628139b1ec5..2aa2a2da9f71 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactory.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpi.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpi.java index d27fa2dfe35a..19c7b85cde6c 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpi.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpi.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticTrustManagerFactory.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticTrustManagerFactory.java new file mode 100644 index 000000000000..2da51afcd594 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/StaticTrustManagerFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; + +import io.netty.handler.ssl.util.SimpleTrustManagerFactory; +import java.security.KeyStore; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import software.amazon.awssdk.annotations.SdkInternalApi; + +@SdkInternalApi +public final class StaticTrustManagerFactory extends SimpleTrustManagerFactory { + private final TrustManager[] trustManagers; + + private StaticTrustManagerFactory(TrustManager[] trustManagers) { + this.trustManagers = trustManagers; + } + + @Override + protected void engineInit(KeyStore keyStore) { + } + + @Override + protected void engineInit(ManagerFactoryParameters managerFactoryParameters) { + } + + @Override + protected TrustManager[] engineGetTrustManagers() { + return trustManagers; + } + + public static TrustManagerFactory create(TrustManager[] trustManagers) { + return new StaticTrustManagerFactory(trustManagers); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandler.java index 7f122a2bd5d5..b4559880ad0b 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -18,8 +18,8 @@ import static software.amazon.awssdk.http.nio.netty.internal.utils.ChannelUtils.getAttribute; import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.timeout.TimeoutException; import java.io.IOException; import java.util.Optional; @@ -36,7 +36,7 @@ */ @SdkInternalApi @ChannelHandler.Sharable -public final class UnusedChannelExceptionHandler extends ChannelHandlerAdapter { +public final class UnusedChannelExceptionHandler extends ChannelInboundHandlerAdapter { public static final UnusedChannelExceptionHandler INSTANCE = new UnusedChannelExceptionHandler(); private static final Logger log = Logger.loggerFor(UnusedChannelExceptionHandler.class); diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/FlushOnReadHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/FlushOnReadHandler.java new file mode 100644 index 000000000000..3cdd06c0c2a5 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/FlushOnReadHandler.java @@ -0,0 +1,47 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This is an HTTP/2 related workaround for an issue where a WINDOW_UPDATE is + * queued but not written to the socket, causing a read() on the channel to + * hang if the remote endpoint thinks our inbound window is 0. + */ +@SdkInternalApi +@ChannelHandler.Sharable +public final class FlushOnReadHandler extends ChannelOutboundHandlerAdapter { + private static final FlushOnReadHandler INSTANCE = new FlushOnReadHandler(); + + private FlushOnReadHandler() { + } + + @Override + public void read(ChannelHandlerContext ctx) { + //Note: order is important, we need to fire the read() event first + // since it's what triggers the WINDOW_UPDATE frame write + ctx.read(); + ctx.channel().parent().flush(); + } + + public static FlushOnReadHandler getInstance() { + return INSTANCE; + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/GoAwayException.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/GoAwayException.java index caa03144ec80..bc9719b3ef38 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/GoAwayException.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/GoAwayException.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -24,11 +24,12 @@ * Exception thrown when a GOAWAY frame is sent by the service. */ @SdkInternalApi -class GoAwayException extends IOException { +public class GoAwayException extends IOException { private final String message; GoAwayException(long errorCode, ByteBuf debugData) { - this.message = String.format("GOAWAY received. Error Code = %d, Debug Data = %s", + this.message = String.format("GOAWAY received from service, requesting this stream be closed. " + + "Error Code = %d, Debug Data = %s", errorCode, debugData.toString(StandardCharsets.UTF_8)); } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayEventListener.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayEventListener.java new file mode 100644 index 000000000000..bb67346a25ea --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayEventListener.java @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.handler.codec.http2.Http2ConnectionAdapter; +import io.netty.handler.codec.http2.Http2GoAwayFrame; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; +import software.amazon.awssdk.utils.Logger; + +/** + * Handles {@link Http2GoAwayFrame}s sent on a connection. This will pass the frame along to the connection's + * {@link Http2MultiplexedChannelPool#handleGoAway(Channel, int, GoAwayException)}. + */ +@SdkInternalApi +public final class Http2GoAwayEventListener extends Http2ConnectionAdapter { + private static final Logger log = Logger.loggerFor(Http2GoAwayEventListener.class); + + private final Channel parentChannel; + + public Http2GoAwayEventListener(Channel parentChannel) { + this.parentChannel = parentChannel; + } + + + @Override + public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) { + Http2MultiplexedChannelPool channelPool = parentChannel.attr(ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL).get(); + GoAwayException exception = new GoAwayException(errorCode, debugData.retain()); + if (channelPool != null) { + channelPool.handleGoAway(parentChannel, lastStreamId, exception); + } else { + log.warn(() -> "GOAWAY received on a connection (" + parentChannel + ") not associated with any multiplexed " + + "channel pool."); + parentChannel.pipeline().fireExceptionCaught(exception); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayFrameHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayFrameHandler.java deleted file mode 100644 index 05d7f5e30800..000000000000 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayFrameHandler.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.codec.http2.Http2GoAwayFrame; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; - -/** - * Handles {@link Http2GoAwayFrame}s sent on a connection. This will pass the frame along to the connection's - * {@link MultiplexedChannelRecord#goAway(Http2GoAwayFrame)} method. - */ -@SdkInternalApi -public class Http2GoAwayFrameHandler extends SimpleChannelInboundHandler { - @Override - protected void channelRead0(ChannelHandlerContext ctx, Http2GoAwayFrame frame) - { - MultiplexedChannelRecord parentConnection = ctx.channel().attr(ChannelAttributeKey.CHANNEL_POOL_RECORD).get(); - if (parentConnection != null) { - parentConnection.goAway(frame); - } - } -} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPool.java index 49b42087654b..0b720f52170b 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,22 +15,43 @@ package software.amazon.awssdk.http.nio.netty.internal.http2; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_POOL_RECORD; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_CONNECTION; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_INITIAL_WINDOW_SIZE; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.MAX_CONCURRENT_STREAMS; import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.doInEventLoop; import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.EventLoop; +import io.netty.channel.EventLoopGroup; import io.netty.channel.pool.ChannelPool; +import io.netty.handler.codec.http2.Http2Connection; +import io.netty.handler.codec.http2.Http2Exception; +import io.netty.handler.codec.http2.Http2LocalFlowController; +import io.netty.handler.codec.http2.Http2Stream; import io.netty.handler.codec.http2.Http2StreamChannelBootstrap; -import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.AttributeKey; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; +import io.netty.util.concurrent.PromiseCombiner; +import java.io.IOException; +import java.nio.channels.ClosedChannelException; +import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; - +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.SdkTestInternalApi; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; import software.amazon.awssdk.http.nio.netty.internal.utils.BetterFixedChannelPool; +import software.amazon.awssdk.utils.Logger; +import software.amazon.awssdk.utils.Validate; /** * {@link ChannelPool} implementation that handles multiplexed streams. Child channels are created @@ -46,151 +67,384 @@ */ @SdkInternalApi public class Http2MultiplexedChannelPool implements ChannelPool { + private static final Logger log = Logger.loggerFor(Http2MultiplexedChannelPool.class); + + /** + * Reference to the {@link MultiplexedChannelRecord} on a channel. + */ + private static final AttributeKey MULTIPLEXED_CHANNEL = AttributeKey.newInstance( + "software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool.MULTIPLEXED_CHANNEL"); + + /** + * Whether a parent channel has been released yet. This guards against double-releasing to the delegate connection pool. + */ + private static final AttributeKey RELEASED = AttributeKey.newInstance( + "software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool.RELEASED"); - private final EventLoop eventLoop; private final ChannelPool connectionPool; - private final long maxConcurrencyPerConnection; - private final ArrayList connections; - private boolean closed = false; + private final EventLoopGroup eventLoopGroup; + private final Set connections; + private final Duration idleConnectionTimeout; + + private AtomicBoolean closed = new AtomicBoolean(false); /** * @param connectionPool Connection pool for parent channels (i.e. the socket channel). - * @param eventLoop Event loop to run all tasks in. - * @param maxConcurrencyPerConnection Max concurrent streams per HTTP/2 connection. */ - Http2MultiplexedChannelPool(ChannelPool connectionPool, - EventLoop eventLoop, - long maxConcurrencyPerConnection) { + Http2MultiplexedChannelPool(ChannelPool connectionPool, EventLoopGroup eventLoopGroup, Duration idleConnectionTimeout) { this.connectionPool = connectionPool; - this.eventLoop = eventLoop; - this.maxConcurrencyPerConnection = maxConcurrencyPerConnection; - // Customers that want an unbounded connection pool may set max concurrency to something like - // Long.MAX_VALUE so we just stick with the initial ArrayList capacity and grow from there. - this.connections = new ArrayList<>(); + this.eventLoopGroup = eventLoopGroup; + this.connections = ConcurrentHashMap.newKeySet(); + this.idleConnectionTimeout = idleConnectionTimeout; } @SdkTestInternalApi Http2MultiplexedChannelPool(ChannelPool connectionPool, - EventLoop eventLoop, - long maxConcurrencyPerConnection, - Collection connections) { - this.connectionPool = connectionPool; - this.eventLoop = eventLoop; - this.maxConcurrencyPerConnection = maxConcurrencyPerConnection; - this.connections = new ArrayList<>(connections); + EventLoopGroup eventLoopGroup, + Set connections, + Duration idleConnectionTimeout) { + this(connectionPool, eventLoopGroup, idleConnectionTimeout); + this.connections.addAll(connections); } @Override public Future acquire() { - return acquire(new DefaultPromise<>(eventLoop)); + return acquire(eventLoopGroup.next().newPromise()); } @Override public Future acquire(Promise promise) { - doInEventLoop(eventLoop, () -> acquire0(promise), promise); - return promise; - } - - private Future acquire0(Promise promise) { - if (closed) { - return promise.setFailure(new IllegalStateException("Channel pool is closed!")); + if (closed.get()) { + return promise.setFailure(new IOException("Channel pool is closed!")); } - for (MultiplexedChannelRecord connection : connections) { - if (connection.availableStreams() > 0) { - connection.acquire(promise); + for (MultiplexedChannelRecord multiplexedChannel : connections) { + if (acquireStreamOnInitializedConnection(multiplexedChannel, promise)) { return promise; } } - // No available streams, establish new connection and add it to list - connections.add(new MultiplexedChannelRecord(connectionPool.acquire(), - maxConcurrencyPerConnection, - this::releaseParentChannel) - .acquire(promise)); + + // No available streams on existing connections, establish new connection and add it to list + acquireStreamOnNewConnection(promise); return promise; } + private void acquireStreamOnNewConnection(Promise promise) { + Future newConnectionAcquire = connectionPool.acquire(); + + newConnectionAcquire.addListener(f -> { + if (!newConnectionAcquire.isSuccess()) { + promise.setFailure(newConnectionAcquire.cause()); + return; + } + + Channel parentChannel = newConnectionAcquire.getNow(); + try { + parentChannel.attr(ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL).set(this); + + // When the protocol future is completed on the new connection, we're ready for new streams to be added to it. + parentChannel.attr(ChannelAttributeKey.PROTOCOL_FUTURE).get() + .thenAccept(protocol -> acquireStreamOnFreshConnection(promise, parentChannel, protocol)) + .exceptionally(throwable -> failAndCloseParent(promise, parentChannel, throwable)); + } catch (Throwable e) { + failAndCloseParent(promise, parentChannel, e); + } + }); + } + + private void acquireStreamOnFreshConnection(Promise promise, Channel parentChannel, Protocol protocol) { + try { + Long maxStreams = parentChannel.attr(MAX_CONCURRENT_STREAMS).get(); + + Validate.isTrue(protocol == Protocol.HTTP2, + "Protocol negotiated on connection (%s) was expected to be HTTP/2, but it " + + "was %s.", parentChannel, Protocol.HTTP1_1); + Validate.isTrue(maxStreams != null, + "HTTP/2 was negotiated on the connection (%s), but the maximum number of " + + "streams was not initialized.", parentChannel); + Validate.isTrue(maxStreams > 0, "Maximum streams were not positive on channel (%s).", parentChannel); + + MultiplexedChannelRecord multiplexedChannel = new MultiplexedChannelRecord(parentChannel, maxStreams, + idleConnectionTimeout); + parentChannel.attr(MULTIPLEXED_CHANNEL).set(multiplexedChannel); + + Promise streamPromise = parentChannel.eventLoop().newPromise(); + + if (!acquireStreamOnInitializedConnection(multiplexedChannel, streamPromise)) { + failAndCloseParent(promise, parentChannel, + new IOException("Connection was closed while creating a new stream.")); + return; + } + + streamPromise.addListener(f -> { + if (!streamPromise.isSuccess()) { + promise.setFailure(streamPromise.cause()); + return; + } + + Channel stream = streamPromise.getNow(); + cacheConnectionForFutureStreams(stream, multiplexedChannel, promise); + }); + } catch (Throwable e) { + failAndCloseParent(promise, parentChannel, e); + } + } + + private void cacheConnectionForFutureStreams(Channel stream, + MultiplexedChannelRecord multiplexedChannel, + Promise promise) { + Channel parentChannel = stream.parent(); + + // Before we cache the connection, make sure that exceptions on the connection will remove it from the cache. + parentChannel.pipeline().addLast(ReleaseOnExceptionHandler.INSTANCE); + connections.add(multiplexedChannel); + + if (closed.get()) { + // Whoops, we were closed while we were setting up. Make sure everything here is cleaned up properly. + failAndCloseParent(promise, parentChannel, + new IOException("Connection pool was closed while creating a new stream.")); + return; + } + + promise.setSuccess(stream); + } + /** - * Releases parent channel on failure and cleans up record from connections list. - * - * @param parentChannel Channel to release. May be null if no channel is established. - * @param record Record to cleanup. + * By default, connection window size is a constant value: + * connectionWindowSize = 65535 + (configureInitialWindowSize - 65535) * 2. + * See https://github.com/netty/netty/blob/5c458c9a98d4d3d0345e58495e017175156d624f/codec-http2/src/main/java/io/netty + * /handler/codec/http2/Http2FrameCodec.java#L255 + * We should expand connection window so that the window size proportional to the number of concurrent streams within the + * connection. + * Note that when {@code WINDOW_UPDATE} will be sent depends on the processedWindow in DefaultHttp2LocalFlowController. */ - private void releaseParentChannel(Channel parentChannel, MultiplexedChannelRecord record) { - doInEventLoop(eventLoop, () -> releaseParentChannel0(parentChannel, record)); - } + private void tryExpandConnectionWindow(Channel parentChannel) { + doInEventLoop(parentChannel.eventLoop(), () -> { + Http2Connection http2Connection = parentChannel.attr(HTTP2_CONNECTION).get(); + Integer initialWindowSize = parentChannel.attr(HTTP2_INITIAL_WINDOW_SIZE).get(); + + Validate.notNull(http2Connection, "http2Connection should not be null on channel " + parentChannel); + Validate.notNull(http2Connection, "initialWindowSize should not be null on channel " + parentChannel); - private void releaseParentChannel0(Channel parentChannel, MultiplexedChannelRecord record) { - if (parentChannel != null) { + Http2Stream connectionStream = http2Connection.connectionStream(); + log.debug(() -> "Expanding connection window size for " + parentChannel + " by " + initialWindowSize); try { - parentChannel.close(); - } finally { - connectionPool.release(parentChannel); + Http2LocalFlowController localFlowController = http2Connection.local().flowController(); + localFlowController.incrementWindowSize(connectionStream, initialWindowSize); + + } catch (Http2Exception e) { + log.warn(() -> "Failed to increment windowSize of connection " + parentChannel, e); } + }); + } + + private Void failAndCloseParent(Promise promise, Channel parentChannel, Throwable exception) { + log.debug(() -> "Channel acquiring failed, closing connection " + parentChannel, exception); + promise.setFailure(exception); + closeAndReleaseParent(parentChannel); + return null; + } + + /** + * Acquire a stream on a connection that has already been initialized. This will return false if the connection cannot have + * any more streams allocated, and true if the stream can be allocated. + * + * This will NEVER complete the provided future when the return value is false. This will ALWAYS complete the provided + * future when the return value is true. + */ + private boolean acquireStreamOnInitializedConnection(MultiplexedChannelRecord channelRecord, Promise promise) { + Promise acquirePromise = channelRecord.getConnection().eventLoop().newPromise(); + + if (!channelRecord.acquireStream(acquirePromise)) { + return false; } - connections.remove(record); + + acquirePromise.addListener(f -> { + try { + if (!acquirePromise.isSuccess()) { + promise.setFailure(acquirePromise.cause()); + return; + } + + Channel channel = acquirePromise.getNow(); + channel.attr(ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL).set(this); + channel.attr(MULTIPLEXED_CHANNEL).set(channelRecord); + promise.setSuccess(channel); + + tryExpandConnectionWindow(channel.parent()); + } catch (Exception e) { + promise.setFailure(e); + } + }); + + return true; } @Override public Future release(Channel childChannel) { - return release(childChannel, new DefaultPromise<>(eventLoop)); + return release(childChannel, childChannel.eventLoop().newPromise()); } @Override - public Future release(Channel channel, Promise promise) { - doInEventLoop(eventLoop, () -> release0(channel, promise), promise); - return promise; + public Future release(Channel childChannel, Promise promise) { + if (childChannel.parent() == null) { + // This isn't a child channel. Oddly enough, this is "expected" and is handled properly by the + // BetterFixedChannelPool AS LONG AS we return an IllegalArgumentException via the promise. + closeAndReleaseParent(childChannel); + return promise.setFailure(new IllegalArgumentException("Channel (" + childChannel + ") is not a child channel.")); + } + + Channel parentChannel = childChannel.parent(); + MultiplexedChannelRecord multiplexedChannel = parentChannel.attr(MULTIPLEXED_CHANNEL).get(); + if (multiplexedChannel == null) { + // This is a child channel, but there is no attached multiplexed channel, which there should be if it was from + // this pool. Close it and log an error. + Exception exception = new IOException("Channel (" + childChannel + ") is not associated with any channel records. " + + "It will be closed, but cannot be released within this pool."); + log.error(exception::getMessage); + childChannel.close(); + return promise.setFailure(exception); + } + + multiplexedChannel.closeAndReleaseChild(childChannel); + + if (multiplexedChannel.canBeClosedAndReleased()) { + // We just closed the last stream in a connection that has reached the end of its life. + return closeAndReleaseParent(parentChannel, null, promise); + } + + return promise.setSuccess(null); } - private void release0(Channel channel, Promise promise) { - if (channel.parent() == null) { - // This is the socket channel, close and release from underlying connection pool - try { - releaseParentChannel(channel); - } finally { - // This channel doesn't technically belong to this pool as it was never acquired directly - promise.setFailure(new IllegalArgumentException("Channel does not belong to this pool")); + private Future closeAndReleaseParent(Channel parentChannel) { + return closeAndReleaseParent(parentChannel, null, parentChannel.eventLoop().newPromise()); + } + + private Future closeAndReleaseParent(Channel parentChannel, Throwable cause) { + return closeAndReleaseParent(parentChannel, cause, parentChannel.eventLoop().newPromise()); + } + + private Future closeAndReleaseParent(Channel parentChannel, Throwable cause, Promise resultPromise) { + if (parentChannel.parent() != null) { + // This isn't a parent channel. Notify it that something is wrong. + Exception exception = new IOException("Channel (" + parentChannel + ") is not a parent channel. It will be closed, " + + "but cannot be released within this pool."); + log.error(exception::getMessage); + parentChannel.close(); + return resultPromise.setFailure(exception); + } + + MultiplexedChannelRecord multiplexedChannel = parentChannel.attr(MULTIPLEXED_CHANNEL).get(); + + // We may not have a multiplexed channel if the parent channel hasn't been fully initialized. + if (multiplexedChannel != null) { + if (cause == null) { + multiplexedChannel.closeChildChannels(); + } else { + multiplexedChannel.closeChildChannels(cause); } - } else { - Channel parentChannel = channel.parent(); - MultiplexedChannelRecord channelRecord = parentChannel.attr(CHANNEL_POOL_RECORD).get(); - channelRecord.release(channel); - channel.close(); - promise.setSuccess(null); + connections.remove(multiplexedChannel); } - } - private void releaseParentChannel(Channel parentChannel) { - MultiplexedChannelRecord channelRecord = parentChannel.attr(CHANNEL_POOL_RECORD).get(); - connections.remove(channelRecord); parentChannel.close(); - connectionPool.release(parentChannel); + if (parentChannel.attr(RELEASED).getAndSet(Boolean.TRUE) == null) { + return connectionPool.release(parentChannel, resultPromise); + } + + return resultPromise.setSuccess(null); + } + + void handleGoAway(Channel parentChannel, int lastStreamId, GoAwayException exception) { + log.debug(() -> "Received GOAWAY on " + parentChannel + " with lastStreamId of " + lastStreamId); + try { + MultiplexedChannelRecord multiplexedChannel = parentChannel.attr(MULTIPLEXED_CHANNEL).get(); + + if (multiplexedChannel != null) { + multiplexedChannel.handleGoAway(lastStreamId, exception); + } else { + // If we don't have a multiplexed channel, the parent channel hasn't been fully initialized. Close it now. + closeAndReleaseParent(parentChannel, exception); + } + } catch (Exception e) { + log.error(() -> "Failed to handle GOAWAY frame on channel " + parentChannel, e); + } } @Override public void close() { - try { - setClosedFlag().await(); - for (MultiplexedChannelRecord c : connections) { - Future f = c.getConnectionFuture(); - f.await(); - if (f.isSuccess()) { - connectionPool.release(f.getNow()).await(); + if (closed.compareAndSet(false, true)) { + Future closeCompleteFuture = doClose(); + + try { + if (!closeCompleteFuture.await(10, TimeUnit.SECONDS)) { + throw new RuntimeException("Event loop didn't close after 10 seconds."); } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + + Throwable exception = closeCompleteFuture.cause(); + if (exception != null) { + throw new RuntimeException("Failed to close channel pool.", exception); } - connectionPool.close(); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - throw new RuntimeException(ie); } } - private Promise setClosedFlag() { - Promise closedFuture = eventLoop.newPromise(); - doInEventLoop(eventLoop, () -> { - closed = true; - closedFuture.setSuccess(null); + private Future doClose() { + EventLoop closeEventLoop = eventLoopGroup.next(); + Promise closeFinishedPromise = closeEventLoop.newPromise(); + + doInEventLoop(closeEventLoop, () -> { + Promise releaseAllChannelsPromise = closeEventLoop.newPromise(); + PromiseCombiner promiseCombiner = new PromiseCombiner(closeEventLoop); + + // Create a copy of the connections to remove while we close them, in case closing updates the original list. + List channelsToRemove = new ArrayList<>(connections); + for (MultiplexedChannelRecord channel : channelsToRemove) { + promiseCombiner.add(closeAndReleaseParent(channel.getConnection())); + } + promiseCombiner.finish(releaseAllChannelsPromise); + + releaseAllChannelsPromise.addListener(f -> { + connectionPool.close(); + closeFinishedPromise.setSuccess(null); + }); }); - return closedFuture; + + return closeFinishedPromise; + } + + @Sharable + private static final class ReleaseOnExceptionHandler extends ChannelDuplexHandler { + private static final ReleaseOnExceptionHandler INSTANCE = new ReleaseOnExceptionHandler(); + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + closeAndReleaseParent(ctx, new ClosedChannelException()); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + if (cause instanceof Http2StreamExceptionHandler.Http2StreamIoException) { + closeConnectionToNewRequests(ctx, cause); + } else { + closeAndReleaseParent(ctx, cause); + } + } + + void closeConnectionToNewRequests(ChannelHandlerContext ctx, Throwable cause) { + MultiplexedChannelRecord multiplexedChannel = ctx.channel().attr(MULTIPLEXED_CHANNEL).get(); + if (multiplexedChannel != null) { + multiplexedChannel.closeToNewStreams(); + } else { + closeAndReleaseParent(ctx, cause); + } + } + + private void closeAndReleaseParent(ChannelHandlerContext ctx, Throwable cause) { + Http2MultiplexedChannelPool pool = ctx.channel().attr(ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL).get(); + pool.closeAndReleaseParent(ctx.channel(), cause); + } } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2PingHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2PingHandler.java new file mode 100644 index 000000000000..bb9f760e2681 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2PingHandler.java @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.http2.DefaultHttp2PingFrame; +import io.netty.handler.codec.http2.Http2PingFrame; +import io.netty.util.concurrent.ScheduledFuture; +import java.util.concurrent.CompletableFuture; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; +import software.amazon.awssdk.utils.Logger; +import software.amazon.awssdk.utils.Validate; + +/** + * Attached to a {@link Channel} to periodically check the health of HTTP2 connections via PING frames. + * + * If a channel is found to be unhealthy, this will invoke {@link ChannelPipeline#fireExceptionCaught(Throwable)}. + */ +@SdkInternalApi +public class Http2PingHandler extends SimpleChannelInboundHandler { + private static final Logger log = Logger.loggerFor(Http2PingHandler.class); + private static final Http2PingFrame DEFAULT_PING_FRAME = new DefaultHttp2PingFrame(0); + + private final long pingTimeoutMillis; + + private ScheduledFuture periodicPing; + private long lastPingSendTime = 0; + private long lastPingAckTime = 0; + + public Http2PingHandler(int pingTimeoutMillis) { + this.pingTimeoutMillis = pingTimeoutMillis; + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) { + CompletableFuture protocolFuture = ctx.channel().attr(ChannelAttributeKey.PROTOCOL_FUTURE).get(); + Validate.validState(protocolFuture != null, "Protocol future must be initialized before handler is added."); + protocolFuture.thenAccept(p -> start(p, ctx)); + } + + private void start(Protocol protocol, ChannelHandlerContext ctx) { + if (protocol == Protocol.HTTP2 && periodicPing == null) { + periodicPing = ctx.channel() + .eventLoop() + .scheduleAtFixedRate(() -> doPeriodicPing(ctx.channel()), 0, pingTimeoutMillis, MILLISECONDS); + } + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) { + stop(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) { + stop(); + ctx.fireChannelInactive(); + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2PingFrame frame) { + if (frame.ack()) { + log.debug(() -> "Received PING ACK from channel " + ctx.channel()); + lastPingAckTime = System.currentTimeMillis(); + } else { + ctx.fireChannelRead(frame); + } + } + + private void doPeriodicPing(Channel channel) { + if (lastPingAckTime <= lastPingSendTime - pingTimeoutMillis) { + long timeSinceLastPingSend = System.currentTimeMillis() - lastPingSendTime; + channelIsUnhealthy(channel, new PingFailedException("Server did not respond to PING after " + + timeSinceLastPingSend + "ms (limit: " + + pingTimeoutMillis + "ms)")); + } else { + sendPing(channel); + } + } + + private void sendPing(Channel channel) { + channel.writeAndFlush(DEFAULT_PING_FRAME).addListener(res -> { + if (!res.isSuccess()) { + log.debug(() -> "Failed to write and flush PING frame to connection", res.cause()); + channelIsUnhealthy(channel, new PingFailedException("Failed to send PING to the service", res.cause())); + } else { + lastPingSendTime = System.currentTimeMillis(); + } + }); + } + + private void channelIsUnhealthy(Channel channel, PingFailedException exception) { + stop(); + channel.pipeline().fireExceptionCaught(exception); + } + + private void stop() { + if (periodicPing != null) { + periodicPing.cancel(false); + periodicPing = null; + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ResetSendingSubscription.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ResetSendingSubscription.java index f50aea83d4d9..534d394373a1 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ResetSendingSubscription.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ResetSendingSubscription.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandler.java index 3d45cae301db..fb243ad79e74 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandler.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import java.util.concurrent.atomic.AtomicReference; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.Protocol; -import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; /** * Configure channel based on the {@link Http2SettingsFrame} received from server @@ -56,22 +55,19 @@ protected void channelRead0(ChannelHandlerContext ctx, Http2SettingsFrame msg) { @Override public void channelUnregistered(ChannelHandlerContext ctx) { if (!channel.attr(PROTOCOL_FUTURE).get().isDone()) { - channelError(new IOException("The channel was closed before the protocol could be determined."), channel); + channelError(new IOException("The channel was closed before the protocol could be determined."), channel, ctx); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - channelError(cause, channel); + channelError(cause, channel, ctx); } - private void channelError(Throwable cause, Channel ch) { + private void channelError(Throwable cause, Channel ch, ChannelHandlerContext ctx) { ch.attr(PROTOCOL_FUTURE).get().completeExceptionally(cause); - MultiplexedChannelRecord record = ch.attr(ChannelAttributeKey.CHANNEL_POOL_RECORD).get(); - // Deliver the exception to any child channels registered to this connection. - if (record != null) { - record.shutdownChildChannels(cause); - } + ctx.fireExceptionCaught(cause); + // Channel status may still be active at this point even if it's not so queue up the close so that status is // accurately updated ch.eventLoop().submit(() -> { diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2StreamExceptionHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2StreamExceptionHandler.java new file mode 100644 index 000000000000..46b9f9df5fdd --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2StreamExceptionHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.http2.Http2Stream; +import io.netty.handler.timeout.TimeoutException; +import java.io.IOException; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Logger; + +/** + * Exception Handler for errors on the Http2 streams. + */ +@ChannelHandler.Sharable +@SdkInternalApi +public final class Http2StreamExceptionHandler extends ChannelInboundHandlerAdapter { + private static final Logger log = Logger.loggerFor(Http2Stream.class); + private static final Http2StreamExceptionHandler INSTANCE = new Http2StreamExceptionHandler(); + + private Http2StreamExceptionHandler() { + } + + public static Http2StreamExceptionHandler create() { + return INSTANCE; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + if (isIoError(cause) && ctx.channel().parent() != null) { + Channel parent = ctx.channel().parent(); + log.debug(() -> "An I/O error occurred on an Http2 stream, notifying the connection channel " + parent); + parent.pipeline().fireExceptionCaught(new Http2StreamIoException("An I/O error occurred on an associated Http2 " + + "stream")); + } + + ctx.fireExceptionCaught(cause); + } + + private boolean isIoError(Throwable cause) { + return cause instanceof TimeoutException || cause instanceof IOException; + } + + static final class Http2StreamIoException extends IOException { + Http2StreamIoException(String message) { + super(message); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ToHttpInboundAdapter.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ToHttpInboundAdapter.java index 4a9ec29a76ea..eecb2054b0c6 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ToHttpInboundAdapter.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2ToHttpInboundAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -38,9 +38,6 @@ @SdkInternalApi public class Http2ToHttpInboundAdapter extends SimpleChannelInboundHandler { - public Http2ToHttpInboundAdapter() { - } - @Override protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) throws Exception { if (frame instanceof Http2DataFrame) { @@ -74,7 +71,7 @@ private void onRstStreamRead(Http2ResetFrame resetFrame, ChannelHandlerContext c ctx.fireExceptionCaught(new Http2ResetException(resetFrame.errorCode())); } - public static class Http2ResetException extends IOException { + public static final class Http2ResetException extends IOException { Http2ResetException(long errorCode) { super(String.format("Connection reset. Error - %s(%d)", Http2Error.valueOf(errorCode).name(), errorCode)); diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPool.java index b4ae604804d2..4d944d3c4e53 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ package software.amazon.awssdk.http.nio.netty.internal.http2; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.MAX_CONCURRENT_STREAMS; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.doInEventLoop; @@ -26,6 +25,7 @@ import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise; +import java.time.Duration; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration; @@ -40,6 +40,7 @@ public class HttpOrHttp2ChannelPool implements ChannelPool { private final ChannelPool delegatePool; private final int maxConcurrency; + private final EventLoopGroup eventLoopGroup; private final EventLoop eventLoop; private final NettyConfiguration configuration; @@ -53,6 +54,7 @@ public HttpOrHttp2ChannelPool(ChannelPool delegatePool, NettyConfiguration configuration) { this.delegatePool = delegatePool; this.maxConcurrency = maxConcurrency; + this.eventLoopGroup = group; this.eventLoop = group.next(); this.configuration = configuration; } @@ -125,18 +127,26 @@ private void failProtocolImplPromise(Throwable e) { }); } - void completeProtocolConfiguration(Channel newChannel, Protocol protocol) { + private void completeProtocolConfiguration(Channel newChannel, Protocol protocol) { doInEventLoop(eventLoop, () -> { if (closed) { - newChannel.close(); - delegatePool.release(newChannel); - protocolImplPromise.setFailure(new IllegalStateException("Pool closed")); + closeAndRelease(newChannel, new IllegalStateException("Pool closed")); } else { - protocolImplPromise.setSuccess(configureProtocol(newChannel, protocol)); + try { + protocolImplPromise.setSuccess(configureProtocol(newChannel, protocol)); + } catch (Throwable e) { + closeAndRelease(newChannel, e); + } } }); } + private void closeAndRelease(Channel newChannel, Throwable e) { + newChannel.close(); + delegatePool.release(newChannel); + protocolImplPromise.setFailure(e); + } + private ChannelPool configureProtocol(Channel newChannel, Protocol protocol) { if (Protocol.HTTP1_1 == protocol) { // For HTTP/1.1 we use a traditional channel pool without multiplexing @@ -149,8 +159,9 @@ private ChannelPool configureProtocol(Channel newChannel, Protocol protocol) { .maxPendingAcquires(configuration.maxPendingConnectionAcquires()) .build(); } else { - ChannelPool h2Pool = new Http2MultiplexedChannelPool( - delegatePool, eventLoop, newChannel.attr(MAX_CONCURRENT_STREAMS).get()); + Duration idleConnectionTimeout = configuration.reapIdleConnections() + ? Duration.ofMillis(configuration.idleTimeoutMillis()) : null; + ChannelPool h2Pool = new Http2MultiplexedChannelPool(delegatePool, eventLoopGroup, idleConnectionTimeout); protocolImpl = BetterFixedChannelPool.builder() .channelPool(h2Pool) .executor(eventLoop) diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapter.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapter.java index 9294ae76d4c5..881f50f64624 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapter.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -71,7 +71,8 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) // Convert and write the headers. Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, false); endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable(); - ctx.write(new DefaultHttp2HeadersFrame(http2Headers), promiseAggregator); + ctx.write(new DefaultHttp2HeadersFrame(http2Headers), promiseAggregator.newPromise()); + } if (!endStream && msg instanceof HttpContent) { @@ -91,11 +92,13 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) ByteBuf content = ((HttpContent) msg).content(); endStream = isLastContent && trailers.isEmpty(); release = false; - ctx.write(new DefaultHttp2DataFrame(content, endStream), promiseAggregator); + ctx.write(new DefaultHttp2DataFrame(content, endStream), promiseAggregator.newPromise()); + if (!trailers.isEmpty()) { // Write trailing headers. - ctx.write(new DefaultHttp2HeadersFrame(http2Trailers, true), promiseAggregator); + ctx.write(new DefaultHttp2HeadersFrame(http2Trailers, true), promiseAggregator.newPromise()); + } ctx.flush(); } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecord.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecord.java index 8943de5caebc..92e2009a7367 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecord.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecord.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,29 +15,31 @@ package software.amazon.awssdk.http.nio.netty.internal.http2; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_POOL_RECORD; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; -import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.asyncPromiseNotifyingBiConsumer; import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.doInEventLoop; -import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.promiseNotifyingListener; -import static software.amazon.awssdk.utils.NumericUtils.saturatedCast; +import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.warnIfNotInEventLoop; import io.netty.channel.Channel; import io.netty.channel.ChannelId; +import io.netty.channel.ChannelOutboundInvoker; import io.netty.handler.codec.http2.Http2GoAwayFrame; import io.netty.handler.codec.http2.Http2StreamChannel; import io.netty.handler.codec.http2.Http2StreamChannelBootstrap; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise; +import io.netty.util.concurrent.ScheduledFuture; import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import java.util.function.BiConsumer; +import java.util.function.Consumer; import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.annotations.SdkTestInternalApi; -import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.UnusedChannelExceptionHandler; +import software.amazon.awssdk.utils.Logger; /** * Contains a {@link Future} for the actual socket channel and tracks available @@ -45,142 +47,270 @@ */ @SdkInternalApi public class MultiplexedChannelRecord { - private final Future connectionFuture; - private final Map childChannels; - private final AtomicLong availableStreams; - private final BiConsumer channelReleaser; + private static final Logger log = Logger.loggerFor(MultiplexedChannelRecord.class); - private volatile Channel connection; - private volatile boolean goAway = false; + private final Channel connection; + private final long maxConcurrencyPerConnection; + private final Long allowedIdleConnectionTimeMillis; - /** - * @param connectionFuture Future for parent socket channel. - * @param maxConcurrencyPerConnection Max streams allowed per connection. - * @param channelReleaser Method to release a channel and record on failure. - */ - MultiplexedChannelRecord(Future connectionFuture, - long maxConcurrencyPerConnection, - BiConsumer channelReleaser) { - this.connectionFuture = connectionFuture; - this.availableStreams = new AtomicLong(maxConcurrencyPerConnection); - this.childChannels = new ConcurrentHashMap<>(saturatedCast(maxConcurrencyPerConnection)); - this.channelReleaser = channelReleaser; - } - - @SdkTestInternalApi - MultiplexedChannelRecord(Future connectionFuture, - Channel connection, - long maxConcurrencyPerConnection, - BiConsumer channelReleaser) { - this.connectionFuture = connectionFuture; - this.childChannels = new ConcurrentHashMap<>(saturatedCast(maxConcurrencyPerConnection)); - this.availableStreams = new AtomicLong(maxConcurrencyPerConnection); - this.channelReleaser = channelReleaser; + private final AtomicLong availableChildChannels; + private volatile long lastReserveAttemptTimeMillis; + + // Only read or write in the connection.eventLoop() + private final Map childChannels = new HashMap<>(); + private ScheduledFuture closeIfIdleTask; + + // Only write in the connection.eventLoop() + private volatile RecordState state = RecordState.OPEN; + + private volatile int lastStreamId; + + MultiplexedChannelRecord(Channel connection, long maxConcurrencyPerConnection, Duration allowedIdleConnectionTime) { this.connection = connection; + this.maxConcurrencyPerConnection = maxConcurrencyPerConnection; + this.availableChildChannels = new AtomicLong(maxConcurrencyPerConnection); + this.allowedIdleConnectionTimeMillis = allowedIdleConnectionTime == null ? null : allowedIdleConnectionTime.toMillis(); } - MultiplexedChannelRecord acquire(Promise channelPromise) { - availableStreams.decrementAndGet(); - if (connection != null) { - createChildChannel(channelPromise); - } else { - connectionFuture.addListener((GenericFutureListener>) future -> { - if (future.isSuccess()) { - connection = future.getNow(); - connection.attr(CHANNEL_POOL_RECORD).set(this); - createChildChannel(channelPromise); + boolean acquireStream(Promise promise) { + if (claimStream()) { + releaseClaimOnFailure(promise); + acquireClaimedStream(promise); + return true; + } + return false; + } + + void acquireClaimedStream(Promise promise) { + doInEventLoop(connection.eventLoop(), () -> { + if (state != RecordState.OPEN) { + String message; + // GOAWAY + if (state == RecordState.CLOSED_TO_NEW) { + message = String.format("Connection %s received GOAWAY with Last Stream ID %d. Unable to open new " + + "streams on this connection.", connection, lastStreamId); } else { - channelPromise.setFailure(future.cause()); - channelReleaser.accept(connection, this); + message = String.format("Connection %s was closed while acquiring new stream.", connection); + } + log.warn(() -> message); + promise.setFailure(new IOException(message)); + return; + } + + Future streamFuture = new Http2StreamChannelBootstrap(connection).open(); + streamFuture.addListener((GenericFutureListener>) future -> { + warnIfNotInEventLoop(connection.eventLoop()); + + if (!future.isSuccess()) { + promise.setFailure(future.cause()); + return; + } + + Http2StreamChannel channel = future.getNow(); + channel.pipeline().addLast(UnusedChannelExceptionHandler.getInstance()); + childChannels.put(channel.id(), channel); + promise.setSuccess(channel); + + if (closeIfIdleTask == null && allowedIdleConnectionTimeMillis != null) { + enableCloseIfIdleTask(); } }); + }, promise); + } + + private void enableCloseIfIdleTask() { + warnIfNotInEventLoop(connection.eventLoop()); + + // Don't poll more frequently than 1 second. Being overly-conservative is okay. Blowing up our CPU is not. + long taskFrequencyMillis = Math.max(allowedIdleConnectionTimeMillis, 1_000); + + closeIfIdleTask = connection.eventLoop().scheduleAtFixedRate(this::closeIfIdle, taskFrequencyMillis, taskFrequencyMillis, + TimeUnit.MILLISECONDS); + connection.closeFuture().addListener(f -> closeIfIdleTask.cancel(false)); + } + + private void releaseClaimOnFailure(Promise promise) { + try { + promise.addListener(f -> { + if (!promise.isSuccess()) { + releaseClaim(); + } + }); + } catch (Throwable e) { + releaseClaim(); + throw e; + } + } + + private void releaseClaim() { + if (availableChildChannels.incrementAndGet() > maxConcurrencyPerConnection) { + assert false; + log.warn(() -> "Child channel count was caught attempting to be increased over max concurrency. " + + "Please report this issue to the AWS SDK for Java team."); + availableChildChannels.decrementAndGet(); } - return this; } /** * Handle a {@link Http2GoAwayFrame} on this connection, preventing new streams from being created on it, and closing any * streams newer than the last-stream-id on the go-away frame. */ - public void goAway(Http2GoAwayFrame frame) { - this.goAway = true; - GoAwayException exception = new GoAwayException(frame.errorCode(), frame.content()); - childChannels.entrySet().stream() - .map(Map.Entry::getValue) - .filter(cc -> cc.stream().id() > frame.lastStreamId()) - .forEach(cc -> cc.eventLoop().execute(() -> shutdownChildChannel(cc, exception))); + void handleGoAway(int lastStreamId, GoAwayException exception) { + doInEventLoop(connection.eventLoop(), () -> { + this.lastStreamId = lastStreamId; + + if (state == RecordState.CLOSED) { + return; + } + + if (state == RecordState.OPEN) { + state = RecordState.CLOSED_TO_NEW; + } + + // Create a copy of the children to close, because fireExceptionCaught may remove from the childChannels. + List childrenToClose = new ArrayList<>(childChannels.values()); + childrenToClose.stream() + .filter(cc -> cc.stream().id() > lastStreamId) + .forEach(cc -> cc.pipeline().fireExceptionCaught(exception)); + }); } /** - * Delivers the exception to all registered child channels, and prohibits new streams being created on this connection. - * - * @param t Exception to deliver. + * Prevent new streams from being acquired from the existing connection. */ - public void shutdownChildChannels(Throwable t) { - this.goAway = true; + void closeToNewStreams() { doInEventLoop(connection.eventLoop(), () -> { - for (Channel childChannel : childChannels.values()) { - shutdownChildChannel(childChannel, t); + if (state == RecordState.OPEN) { + state = RecordState.CLOSED_TO_NEW; } }); } - private void shutdownChildChannel(Channel childChannel, Throwable t) { - childChannel.pipeline().fireExceptionCaught(t); + /** + * Close all registered child channels, and prohibit new streams from being created on this connection. + */ + void closeChildChannels() { + closeAndExecuteOnChildChannels(ChannelOutboundInvoker::close); } /** - * Bootstraps a child stream channel from the parent socket channel. Done in parent channel event loop. - * - * @param channelPromise Promise to notify when channel is available. + * Delivers the exception to all registered child channels, and prohibits new streams being created on this connection. */ - private void createChildChannel(Promise channelPromise) { - doInEventLoop(connection.eventLoop(), () -> createChildChannel0(channelPromise), channelPromise); + void closeChildChannels(Throwable t) { + closeAndExecuteOnChildChannels(ch -> ch.pipeline().fireExceptionCaught(decorateConnectionException(t))); } - private void createChildChannel0(Promise channelPromise) { - if (goAway) { - channelPromise.tryFailure(new IOException("No streams are available on this connection.")); - } else { - // Once protocol future is notified then parent pipeline is configured and ready to go - connection.attr(PROTOCOL_FUTURE).get() - .whenComplete(asyncPromiseNotifyingBiConsumer(bootstrapChildChannel(), channelPromise)); + private Throwable decorateConnectionException(Throwable t) { + String message = "An error occurred on the connection: " + t.getMessage(); + if (t instanceof IOException) { + return new IOException(message, t); } + + return new Throwable(message, t); } - /** - * Bootstraps the child stream channel and notifies the Promise on success or failure. - * - * @return BiConsumer that will bootstrap the child channel. - */ - private BiConsumer> bootstrapChildChannel() { - return (s, p) -> new Http2StreamChannelBootstrap(connection) - .open() - .addListener((GenericFutureListener>) future -> { - if (future.isSuccess()) { - Http2StreamChannel channel = future.getNow(); - childChannels.put(channel.id(), channel); - } else { - if (!connection.isActive()) { - channelReleaser.accept(connection, this); - } - availableStreams.incrementAndGet(); - } - }) - .addListener(promiseNotifyingListener(p)); + private void closeAndExecuteOnChildChannels(Consumer childChannelConsumer) { + doInEventLoop(connection.eventLoop(), () -> { + if (state == RecordState.CLOSED) { + return; + } + state = RecordState.CLOSED; + + // Create a copy of the children, because they may be modified by the consumer. + List childrenToClose = new ArrayList<>(childChannels.values()); + for (Channel childChannel : childrenToClose) { + childChannelConsumer.accept(childChannel); + } + }); + } + + void closeAndReleaseChild(Channel childChannel) { + childChannel.close(); + doInEventLoop(connection.eventLoop(), () -> { + childChannels.remove(childChannel.id()); + releaseClaim(); + }); + } + + private void closeIfIdle() { + warnIfNotInEventLoop(connection.eventLoop()); + + // Don't close if we have child channels. + if (!childChannels.isEmpty()) { + return; + } + + // Don't close if there have been any reserves attempted since the idle connection time. + long nonVolatileLastReserveAttemptTimeMillis = lastReserveAttemptTimeMillis; + if (nonVolatileLastReserveAttemptTimeMillis > System.currentTimeMillis() - allowedIdleConnectionTimeMillis) { + return; + } + + // Cut off new streams from being acquired from this connection by setting the number of available channels to 0. + // This write may fail if a reservation has happened since we checked the lastReserveAttemptTime. + if (!availableChildChannels.compareAndSet(maxConcurrencyPerConnection, 0)) { + return; + } + + // If we've been closed, no need to shut down. + if (state != RecordState.OPEN) { + return; + } + + log.debug(() -> "Connection " + connection + " has been idle for " + + (System.currentTimeMillis() - nonVolatileLastReserveAttemptTimeMillis) + "ms and will be shut down."); + + // Mark ourselves as closed + state = RecordState.CLOSED; + + // Start the shutdown process by closing the connection (which should be noticed by the connection pool) + connection.close(); } - void release(Channel channel) { - availableStreams.incrementAndGet(); - childChannels.remove(channel.id()); + public Channel getConnection() { + return connection; } - public Future getConnectionFuture() { - return connectionFuture; + private boolean claimStream() { + lastReserveAttemptTimeMillis = System.currentTimeMillis(); + for (int attempt = 0; attempt < 5; ++attempt) { + + if (state != RecordState.OPEN) { + return false; + } + + long currentlyAvailable = availableChildChannels.get(); + + if (currentlyAvailable <= 0) { + return false; + } + if (availableChildChannels.compareAndSet(currentlyAvailable, currentlyAvailable - 1)) { + return true; + } + } + + return false; } - long availableStreams() { - return goAway ? 0 : availableStreams.get(); + boolean canBeClosedAndReleased() { + return state != RecordState.OPEN && availableChildChannels.get() == maxConcurrencyPerConnection; } + private enum RecordState { + /** + * The connection is open and new streams may be acquired from it, if they are available. + */ + OPEN, + + /** + * The connection is open, but new streams may not be acquired from it. This occurs when a connection is being + * shut down (e.g. after it has received a GOAWAY frame), but all streams haven't been closed yet. + */ + CLOSED_TO_NEW, + + /** + * The connection is closed and new streams may not be acquired from it. + */ + CLOSED + } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/PingFailedException.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/PingFailedException.java new file mode 100644 index 000000000000..80103860f327 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/PingFailedException.java @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import java.io.IOException; +import software.amazon.awssdk.annotations.SdkInternalApi; + +@SdkInternalApi +public final class PingFailedException extends IOException { + PingFailedException(String msg) { + super(msg); + } + + PingFailedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/PingTracker.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/PingTracker.java new file mode 100644 index 000000000000..414ec20d834f --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/http2/PingTracker.java @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import io.netty.util.concurrent.ScheduledFuture; +import java.util.function.Supplier; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Tracking the status after sending out the PING frame + */ +@SdkInternalApi +public final class PingTracker { + + private final Supplier> timerFutureSupplier; + private ScheduledFuture pingTimerFuture; + + PingTracker(Supplier> timerFutureSupplier) { + this.timerFutureSupplier = timerFutureSupplier; + } + + public void start() { + pingTimerFuture = timerFutureSupplier.get(); + } + + public void cancel() { + if (pingTimerFuture != null) { + pingTimerFuture.cancel(false); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/CancelledSubscriber.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/CancelledSubscriber.java new file mode 100644 index 000000000000..b2edb8b60c93 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/CancelledSubscriber.java @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * A cancelled subscriber. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public final class CancelledSubscriber implements Subscriber { + + @Override + public void onSubscribe(Subscription subscription) { + if (subscription == null) { + throw new NullPointerException("Null subscription"); + } else { + subscription.cancel(); + } + } + + @Override + public void onNext(T t) { + } + + @Override + public void onError(Throwable error) { + if (error == null) { + throw new NullPointerException("Null error published"); + } + } + + @Override + public void onComplete() { + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DefaultStreamedHttpRequest.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DefaultStreamedHttpRequest.java new file mode 100644 index 000000000000..7d9a82c9bed9 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DefaultStreamedHttpRequest.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import java.util.Objects; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * A default streamed HTTP request. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public class DefaultStreamedHttpRequest extends DefaultHttpRequest implements StreamedHttpRequest { + + private final Publisher stream; + + public DefaultStreamedHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, Publisher stream) { + super(httpVersion, method, uri); + this.stream = stream; + } + + public DefaultStreamedHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, boolean validateHeaders, + Publisher stream) { + super(httpVersion, method, uri, validateHeaders); + this.stream = stream; + } + + @Override + public void subscribe(Subscriber subscriber) { + stream.subscribe(subscriber); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + DefaultStreamedHttpRequest that = (DefaultStreamedHttpRequest) o; + + return Objects.equals(stream, that.stream); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (stream != null ? stream.hashCode() : 0); + return result; + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DefaultStreamedHttpResponse.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DefaultStreamedHttpResponse.java new file mode 100644 index 000000000000..dc6fc82cb69b --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DefaultStreamedHttpResponse.java @@ -0,0 +1,81 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import java.util.Objects; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * A default streamed HTTP response. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public class DefaultStreamedHttpResponse extends DefaultHttpResponse implements StreamedHttpResponse { + + private final Publisher stream; + + public DefaultStreamedHttpResponse(HttpVersion version, HttpResponseStatus status, Publisher stream) { + super(version, status); + this.stream = stream; + } + + public DefaultStreamedHttpResponse(HttpVersion version, HttpResponseStatus status, boolean validateHeaders, + Publisher stream) { + super(version, status, validateHeaders); + this.stream = stream; + } + + @Override + public void subscribe(Subscriber subscriber) { + stream.subscribe(subscriber); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + DefaultStreamedHttpResponse that = (DefaultStreamedHttpResponse) o; + + return Objects.equals(stream, that.stream); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (stream != null ? stream.hashCode() : 0); + return result; + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpMessage.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpMessage.java new file mode 100644 index 000000000000..5cd6d3b68687 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpMessage.java @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.DecoderResult; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.http.HttpVersion; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +class DelegateHttpMessage implements HttpMessage { + protected final HttpMessage message; + + DelegateHttpMessage(HttpMessage message) { + this.message = message; + } + + @Override + @Deprecated + public HttpVersion getProtocolVersion() { + return message.protocolVersion(); + } + + @Override + public HttpVersion protocolVersion() { + return message.protocolVersion(); + } + + @Override + public HttpMessage setProtocolVersion(HttpVersion version) { + message.setProtocolVersion(version); + return this; + } + + @Override + public HttpHeaders headers() { + return message.headers(); + } + + @Override + @Deprecated + public DecoderResult getDecoderResult() { + return message.decoderResult(); + } + + @Override + public DecoderResult decoderResult() { + return message.decoderResult(); + } + + @Override + public void setDecoderResult(DecoderResult result) { + message.setDecoderResult(result); + } + + @Override + public String toString() { + return this.getClass().getName() + "(" + message.toString() + ")"; + } + +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpRequest.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpRequest.java new file mode 100644 index 000000000000..213a68824ff7 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpRequest.java @@ -0,0 +1,80 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpVersion; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +class DelegateHttpRequest extends DelegateHttpMessage implements HttpRequest { + + protected final HttpRequest request; + + DelegateHttpRequest(HttpRequest request) { + super(request); + this.request = request; + } + + @Override + public HttpRequest setMethod(HttpMethod method) { + request.setMethod(method); + return this; + } + + @Override + public HttpRequest setUri(String uri) { + request.setUri(uri); + return this; + } + + @Override + @Deprecated + public HttpMethod getMethod() { + return request.method(); + } + + @Override + public HttpMethod method() { + return request.method(); + } + + @Override + @Deprecated + public String getUri() { + return request.uri(); + } + + @Override + public String uri() { + return request.uri(); + } + + @Override + public HttpRequest setProtocolVersion(HttpVersion version) { + super.setProtocolVersion(version); + return this; + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpResponse.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpResponse.java new file mode 100644 index 000000000000..e446aeb6df52 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateHttpResponse.java @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +class DelegateHttpResponse extends DelegateHttpMessage implements HttpResponse { + + protected final HttpResponse response; + + DelegateHttpResponse(HttpResponse response) { + super(response); + this.response = response; + } + + @Override + public HttpResponse setStatus(HttpResponseStatus status) { + response.setStatus(status); + return this; + } + + @Override + @Deprecated + public HttpResponseStatus getStatus() { + return response.status(); + } + + @Override + public HttpResponseStatus status() { + return response.status(); + } + + @Override + public HttpResponse setProtocolVersion(HttpVersion version) { + super.setProtocolVersion(version); + return this; + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateStreamedHttpRequest.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateStreamedHttpRequest.java new file mode 100644 index 000000000000..c3c495c2e724 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateStreamedHttpRequest.java @@ -0,0 +1,46 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpRequest; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +final class DelegateStreamedHttpRequest extends DelegateHttpRequest implements StreamedHttpRequest { + + private final Publisher stream; + + DelegateStreamedHttpRequest(HttpRequest request, Publisher stream) { + super(request); + this.stream = stream; + } + + @Override + public void subscribe(Subscriber subscriber) { + stream.subscribe(subscriber); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateStreamedHttpResponse.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateStreamedHttpResponse.java new file mode 100644 index 000000000000..5a5612679205 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/DelegateStreamedHttpResponse.java @@ -0,0 +1,46 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpResponse; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +final class DelegateStreamedHttpResponse extends DelegateHttpResponse implements StreamedHttpResponse { + + private final Publisher stream; + + DelegateStreamedHttpResponse(HttpResponse response, Publisher stream) { + super(response); + this.stream = stream; + } + + @Override + public void subscribe(Subscriber subscriber) { + stream.subscribe(subscriber); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/EmptyHttpRequest.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/EmptyHttpRequest.java new file mode 100644 index 000000000000..08c1eb9649b7 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/EmptyHttpRequest.java @@ -0,0 +1,160 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.DefaultHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +class EmptyHttpRequest extends DelegateHttpRequest implements FullHttpRequest { + + EmptyHttpRequest(HttpRequest request) { + super(request); + } + + @Override + public FullHttpRequest setUri(String uri) { + super.setUri(uri); + return this; + } + + @Override + public FullHttpRequest setMethod(HttpMethod method) { + super.setMethod(method); + return this; + } + + @Override + public FullHttpRequest setProtocolVersion(HttpVersion version) { + super.setProtocolVersion(version); + return this; + } + + @Override + public FullHttpRequest copy() { + if (request instanceof FullHttpRequest) { + return new EmptyHttpRequest(((FullHttpRequest) request).copy()); + } else { + DefaultHttpRequest copy = new DefaultHttpRequest(protocolVersion(), method(), uri()); + copy.headers().set(headers()); + return new EmptyHttpRequest(copy); + } + } + + @Override + public FullHttpRequest retain(int increment) { + ReferenceCountUtil.retain(message, increment); + return this; + } + + @Override + public FullHttpRequest retain() { + ReferenceCountUtil.retain(message); + return this; + } + + @Override + public FullHttpRequest touch() { + if (request instanceof FullHttpRequest) { + return ((FullHttpRequest) request).touch(); + } else { + return this; + } + } + + @Override + public FullHttpRequest touch(Object o) { + if (request instanceof FullHttpRequest) { + return ((FullHttpRequest) request).touch(o); + } else { + return this; + } + } + + @Override + public HttpHeaders trailingHeaders() { + return new DefaultHttpHeaders(); + } + + @Override + public FullHttpRequest duplicate() { + if (request instanceof FullHttpRequest) { + return ((FullHttpRequest) request).duplicate(); + } else { + return this; + } + } + + @Override + public FullHttpRequest retainedDuplicate() { + if (request instanceof FullHttpRequest) { + return ((FullHttpRequest) request).retainedDuplicate(); + } else { + return this; + } + } + + @Override + public FullHttpRequest replace(ByteBuf byteBuf) { + if (message instanceof FullHttpRequest) { + return ((FullHttpRequest) request).replace(byteBuf); + } else { + return this; + } + } + + @Override + public ByteBuf content() { + return Unpooled.EMPTY_BUFFER; + } + + @Override + public int refCnt() { + if (message instanceof ReferenceCounted) { + return ((ReferenceCounted) message).refCnt(); + } else { + return 1; + } + } + + @Override + public boolean release() { + return ReferenceCountUtil.release(message); + } + + @Override + public boolean release(int decrement) { + return ReferenceCountUtil.release(message, decrement); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/EmptyHttpResponse.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/EmptyHttpResponse.java new file mode 100644 index 000000000000..b514b5639161 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/EmptyHttpResponse.java @@ -0,0 +1,154 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.DefaultHttpResponse; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.ReferenceCounted; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +class EmptyHttpResponse extends DelegateHttpResponse implements FullHttpResponse { + + EmptyHttpResponse(HttpResponse response) { + super(response); + } + + @Override + public FullHttpResponse setStatus(HttpResponseStatus status) { + super.setStatus(status); + return this; + } + + @Override + public FullHttpResponse setProtocolVersion(HttpVersion version) { + super.setProtocolVersion(version); + return this; + } + + @Override + public FullHttpResponse copy() { + if (response instanceof FullHttpResponse) { + return new EmptyHttpResponse(((FullHttpResponse) response).copy()); + } else { + DefaultHttpResponse copy = new DefaultHttpResponse(protocolVersion(), status()); + copy.headers().set(headers()); + return new EmptyHttpResponse(copy); + } + } + + @Override + public FullHttpResponse retain(int increment) { + ReferenceCountUtil.retain(message, increment); + return this; + } + + @Override + public FullHttpResponse retain() { + ReferenceCountUtil.retain(message); + return this; + } + + @Override + public FullHttpResponse touch() { + if (response instanceof FullHttpResponse) { + return ((FullHttpResponse) response).touch(); + } else { + return this; + } + } + + @Override + public FullHttpResponse touch(Object o) { + if (response instanceof FullHttpResponse) { + return ((FullHttpResponse) response).touch(o); + } else { + return this; + } + } + + @Override + public HttpHeaders trailingHeaders() { + return new DefaultHttpHeaders(); + } + + @Override + public FullHttpResponse duplicate() { + if (response instanceof FullHttpResponse) { + return ((FullHttpResponse) response).duplicate(); + } else { + return this; + } + } + + @Override + public FullHttpResponse retainedDuplicate() { + if (response instanceof FullHttpResponse) { + return ((FullHttpResponse) response).retainedDuplicate(); + } else { + return this; + } + } + + @Override + public FullHttpResponse replace(ByteBuf byteBuf) { + if (response instanceof FullHttpResponse) { + return ((FullHttpResponse) response).replace(byteBuf); + } else { + return this; + } + } + + @Override + public ByteBuf content() { + return Unpooled.EMPTY_BUFFER; + } + + @Override + public int refCnt() { + if (message instanceof ReferenceCounted) { + return ((ReferenceCounted) message).refCnt(); + } else { + return 1; + } + } + + @Override + public boolean release() { + return ReferenceCountUtil.release(message); + } + + @Override + public boolean release(int decrement) { + return ReferenceCountUtil.release(message, decrement); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerPublisher.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerPublisher.java new file mode 100644 index 000000000000..0a29dab186de --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerPublisher.java @@ -0,0 +1,510 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelPipeline; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.concurrent.EventExecutor; +import io.netty.util.internal.TypeParameterMatcher; +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicBoolean; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Publisher for a Netty Handler. + * + * This publisher supports only one subscriber. + * + * All interactions with the subscriber are done from the handlers executor, hence, they provide the same happens before + * semantics that Netty provides. + * + * The handler publishes all messages that match the type as specified by the passed in class. Any non matching messages + * are forwarded to the next handler. + * + * The publisher will signal complete if it receives a channel inactive event. + * + * The publisher will release any messages that it drops (for example, messages that are buffered when the subscriber + * cancels), but other than that, it does not release any messages. It is up to the subscriber to release messages. + * + * If the subscriber cancels, the publisher will send a close event up the channel pipeline. + * + * All errors will short circuit the buffer, and cause publisher to immediately call the subscribers onError method, + * dropping the buffer. + * + * The publisher can be subscribed to or placed in a handler chain in any order. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + */ +@SdkInternalApi +public class HandlerPublisher extends ChannelDuplexHandler implements Publisher { + /** + * Used for buffering a completion signal. + */ + private static final Object COMPLETE = new Object() { + @Override + public String toString() { + return "COMPLETE"; + } + }; + + private final EventExecutor executor; + private final TypeParameterMatcher matcher; + + private final Queue buffer = new LinkedList<>(); + + /** + * Whether a subscriber has been provided. This is used to detect whether two subscribers are subscribing + * simultaneously. + */ + private final AtomicBoolean hasSubscriber = new AtomicBoolean(); + + private State state = HandlerPublisher.State.NO_SUBSCRIBER_OR_CONTEXT; + + private volatile Subscriber subscriber; + private ChannelHandlerContext ctx; + private long outstandingDemand = 0; + private Throwable noSubscriberError; + + /** + * Create a handler publisher. + * + * The supplied executor must be the same event loop as the event loop that this handler is eventually registered + * with, if not, an exception will be thrown when the handler is registered. + * + * @param executor The executor to execute asynchronous events from the subscriber on. + * @param subscriberMessageType The type of message this publisher accepts. + */ + public HandlerPublisher(EventExecutor executor, Class subscriberMessageType) { + this.executor = executor; + this.matcher = TypeParameterMatcher.get(subscriberMessageType); + } + + /** + * Returns {@code true} if the given message should be handled. If {@code false} it will be passed to the next + * {@link ChannelInboundHandler} in the {@link ChannelPipeline}. + * + * @param msg The message to check. + * @return True if the message should be accepted. + */ + protected boolean acceptInboundMessage(Object msg) throws Exception { + return matcher.match(msg); + } + + /** + * Override to handle when a subscriber cancels the subscription. + * + * By default, this method will simply close the channel. + */ + protected void cancelled() { + ctx.close(); + } + + /** + * Override to intercept when demand is requested. + * + * By default, a channel read is invoked. + */ + protected void requestDemand() { + ctx.read(); + } + + enum State { + /** + * Initial state. There's no subscriber, and no context. + */ + NO_SUBSCRIBER_OR_CONTEXT, + + /** + * A subscriber has been provided, but no context has been provided. + */ + NO_CONTEXT, + + /** + * A context has been provided, but no subscriber has been provided. + */ + NO_SUBSCRIBER, + + /** + * An error has been received, but there's no subscriber to receive it. + */ + NO_SUBSCRIBER_ERROR, + + /** + * There is no demand, and we have nothing buffered. + */ + IDLE, + + /** + * There is no demand, and we're buffering elements. + */ + BUFFERING, + + /** + * We have nothing buffered, but there is demand. + */ + DEMANDING, + + /** + * The stream is complete, however there are still elements buffered for which no demand has come from the subscriber. + */ + DRAINING, + + /** + * We're done, in the terminal state. + */ + DONE + } + + @Override + public void subscribe(final Subscriber subscriber) { + if (subscriber == null) { + throw new NullPointerException("Null subscriber"); + } + + if (!hasSubscriber.compareAndSet(false, true)) { + // Must call onSubscribe first. + subscriber.onSubscribe(new Subscription() { + @Override + public void request(long n) { + } + + @Override + public void cancel() { + } + }); + subscriber.onError(new IllegalStateException("This publisher only supports one subscriber")); + } else { + executor.execute(new Runnable() { + @Override + public void run() { + provideSubscriber(subscriber); + } + }); + } + } + + private void provideSubscriber(Subscriber subscriber) { + this.subscriber = subscriber; + switch (state) { + case NO_SUBSCRIBER_OR_CONTEXT: + state = HandlerPublisher.State.NO_CONTEXT; + break; + case NO_SUBSCRIBER: + if (buffer.isEmpty()) { + state = HandlerPublisher.State.IDLE; + } else { + state = HandlerPublisher.State.BUFFERING; + } + subscriber.onSubscribe(new ChannelSubscription()); + break; + case DRAINING: + subscriber.onSubscribe(new ChannelSubscription()); + break; + case NO_SUBSCRIBER_ERROR: + cleanup(); + state = HandlerPublisher.State.DONE; + subscriber.onSubscribe(new ChannelSubscription()); + subscriber.onError(noSubscriberError); + break; + default: + // Do nothing + } + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + // If the channel is not yet registered, then it's not safe to invoke any methods on it, eg read() or close() + // So don't provide the context until it is registered. + if (ctx.channel().isRegistered()) { + provideChannelContext(ctx); + } + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + provideChannelContext(ctx); + ctx.fireChannelRegistered(); + } + + private void provideChannelContext(ChannelHandlerContext ctx) { + switch (state) { + case NO_SUBSCRIBER_OR_CONTEXT: + verifyRegisteredWithRightExecutor(ctx); + this.ctx = ctx; + // It's set, we don't have a subscriber + state = HandlerPublisher.State.NO_SUBSCRIBER; + break; + case NO_CONTEXT: + verifyRegisteredWithRightExecutor(ctx); + this.ctx = ctx; + state = HandlerPublisher.State.IDLE; + subscriber.onSubscribe(new ChannelSubscription()); + break; + default: + // Ignore, this could be invoked twice by both handlerAdded and channelRegistered. + } + } + + private void verifyRegisteredWithRightExecutor(ChannelHandlerContext ctx) { + if (!executor.inEventLoop()) { + throw new IllegalArgumentException("Channel handler MUST be registered with the same EventExecutor that it is " + + "created with."); + } + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + // If we subscribed before the channel was active, then our read would have been ignored. + if (state == HandlerPublisher.State.DEMANDING) { + requestDemand(); + } + ctx.fireChannelActive(); + } + + private void receivedDemand(long demand) { + switch (state) { + case BUFFERING: + case DRAINING: + if (addDemand(demand)) { + flushBuffer(); + } + break; + + case DEMANDING: + addDemand(demand); + break; + + case IDLE: + if (addDemand(demand)) { + // Important to change state to demanding before doing a read, in case we get a synchronous + // read back. + state = HandlerPublisher.State.DEMANDING; + requestDemand(); + } + break; + default: + + } + } + + private boolean addDemand(long demand) { + + if (demand <= 0) { + illegalDemand(); + return false; + } else { + if (outstandingDemand < Long.MAX_VALUE) { + outstandingDemand += demand; + if (outstandingDemand < 0) { + outstandingDemand = Long.MAX_VALUE; + } + } + return true; + } + } + + private void illegalDemand() { + cleanup(); + subscriber.onError(new IllegalArgumentException("Request for 0 or negative elements in violation of Section 3.9 " + + "of the Reactive Streams specification")); + ctx.close(); + state = HandlerPublisher.State.DONE; + } + + private void flushBuffer() { + while (!buffer.isEmpty() && (outstandingDemand > 0 || outstandingDemand == Long.MAX_VALUE)) { + publishMessage(buffer.remove()); + } + if (buffer.isEmpty()) { + if (outstandingDemand > 0) { + if (state == HandlerPublisher.State.BUFFERING) { + state = HandlerPublisher.State.DEMANDING; + } // otherwise we're draining + requestDemand(); + } else if (state == HandlerPublisher.State.BUFFERING) { + state = HandlerPublisher.State.IDLE; + } + } + } + + private void receivedCancel() { + switch (state) { + case BUFFERING: + case DEMANDING: + case IDLE: + cancelled(); + state = HandlerPublisher.State.DONE; + break; + case DRAINING: + state = HandlerPublisher.State.DONE; + break; + default: + // ignore + } + cleanup(); + subscriber = null; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { + if (acceptInboundMessage(message)) { + switch (state) { + case IDLE: + buffer.add(message); + state = HandlerPublisher.State.BUFFERING; + break; + case NO_SUBSCRIBER: + case BUFFERING: + buffer.add(message); + break; + case DEMANDING: + publishMessage(message); + break; + case DRAINING: + case DONE: + ReferenceCountUtil.release(message); + break; + case NO_CONTEXT: + case NO_SUBSCRIBER_OR_CONTEXT: + throw new IllegalStateException("Message received before added to the channel context"); + default: + // Ignore + } + } else { + ctx.fireChannelRead(message); + } + } + + private void publishMessage(Object message) { + if (COMPLETE.equals(message)) { + subscriber.onComplete(); + state = HandlerPublisher.State.DONE; + } else { + @SuppressWarnings("unchecked") + T next = (T) message; + subscriber.onNext(next); + if (outstandingDemand < Long.MAX_VALUE) { + outstandingDemand--; + if (outstandingDemand == 0 && state != HandlerPublisher.State.DRAINING) { + if (buffer.isEmpty()) { + state = HandlerPublisher.State.IDLE; + } else { + state = HandlerPublisher.State.BUFFERING; + } + } + } + } + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + if (state == HandlerPublisher.State.DEMANDING) { + requestDemand(); + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + complete(); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + complete(); + } + + private void complete() { + switch (state) { + case NO_SUBSCRIBER: + case BUFFERING: + buffer.add(COMPLETE); + state = HandlerPublisher.State.DRAINING; + break; + case DEMANDING: + case IDLE: + subscriber.onComplete(); + state = HandlerPublisher.State.DONE; + break; + case NO_SUBSCRIBER_ERROR: + // Ignore, we're already going to complete the stream with an error + // when the subscriber subscribes. + break; + default: + // Ignore + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + switch (state) { + case NO_SUBSCRIBER: + noSubscriberError = cause; + state = HandlerPublisher.State.NO_SUBSCRIBER_ERROR; + cleanup(); + break; + case BUFFERING: + case DEMANDING: + case IDLE: + case DRAINING: + state = HandlerPublisher.State.DONE; + cleanup(); + subscriber.onError(cause); + break; + default: + // Ignore + } + } + + /** + * Release all elements from the buffer. + */ + private void cleanup() { + while (!buffer.isEmpty()) { + ReferenceCountUtil.release(buffer.remove()); + } + } + + private class ChannelSubscription implements Subscription { + @Override + public void request(final long demand) { + executor.execute(new Runnable() { + @Override + public void run() { + receivedDemand(demand); + } + }); + } + + @Override + public void cancel() { + executor.execute(new Runnable() { + @Override + public void run() { + receivedCancel(); + } + }); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriber.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriber.java new file mode 100644 index 000000000000..81e2a648a145 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriber.java @@ -0,0 +1,306 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.concurrent.EventExecutor; +import java.util.concurrent.atomic.AtomicBoolean; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.nio.netty.internal.utils.OrderedWriteChannelHandlerContext; +import software.amazon.awssdk.utils.Validate; + +/** + * Subscriber that publishes received messages to the handler pipeline. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public class HandlerSubscriber extends ChannelDuplexHandler implements Subscriber { + static final long DEFAULT_LOW_WATERMARK = 4; + static final long DEFAULT_HIGH_WATERMARK = 16; + + private final EventExecutor executor; + private final long demandLowWatermark; + private final long demandHighWatermark; + + private final AtomicBoolean hasSubscription = new AtomicBoolean(); + + private volatile Subscription subscription; + private volatile ChannelHandlerContext ctx; + + private State state = HandlerSubscriber.State.NO_SUBSCRIPTION_OR_CONTEXT; + private long outstandingDemand = 0; + private ChannelFuture lastWriteFuture; + + /** + * Create a new handler subscriber. + * + * The supplied executor must be the same event loop as the event loop that this handler is eventually registered + * with, if not, an exception will be thrown when the handler is registered. + * + * @param executor The executor to execute asynchronous events from the publisher on. + * @param demandLowWatermark The low watermark for demand. When demand drops below this, more will be requested. + * @param demandHighWatermark The high watermark for demand. This is the maximum that will be requested. + */ + public HandlerSubscriber(EventExecutor executor, long demandLowWatermark, long demandHighWatermark) { + this.executor = executor; + this.demandLowWatermark = demandLowWatermark; + this.demandHighWatermark = demandHighWatermark; + } + + /** + * Create a new handler subscriber with the default low and high watermarks. + * + * The supplied executor must be the same event loop as the event loop that this handler is eventually registered + * with, if not, an exception will be thrown when the handler is registered. + * + * @param executor The executor to execute asynchronous events from the publisher on. + * @see #HandlerSubscriber(EventExecutor, long, long) + */ + public HandlerSubscriber(EventExecutor executor) { + this(executor, DEFAULT_LOW_WATERMARK, DEFAULT_HIGH_WATERMARK); + } + + /** + * Override for custom error handling. By default, it closes the channel. + * + * @param error The error to handle. + */ + protected void error(Throwable error) { + doClose(); + } + + /** + * Override for custom completion handling. By default, it closes the channel. + */ + protected void complete() { + doClose(); + } + + enum State { + NO_SUBSCRIPTION_OR_CONTEXT, + NO_SUBSCRIPTION, + NO_CONTEXT, + INACTIVE, + RUNNING, + CANCELLED, + COMPLETE + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + verifyRegisteredWithRightExecutor(ctx); + + // Ensure that writes to the context happen consecutively, even if they're performed from within the event loop. + // See https://github.com/netty/netty/issues/7783 + ctx = OrderedWriteChannelHandlerContext.wrap(ctx); + + switch (state) { + case NO_SUBSCRIPTION_OR_CONTEXT: + this.ctx = ctx; + // We were in no subscription or context, now we just don't have a subscription. + state = HandlerSubscriber.State.NO_SUBSCRIPTION; + break; + case NO_CONTEXT: + this.ctx = ctx; + // We were in no context, we're now fully initialised + maybeStart(); + break; + case COMPLETE: + // We are complete, close + state = HandlerSubscriber.State.COMPLETE; + ctx.close(); + break; + default: + throw new IllegalStateException("This handler must only be added to a pipeline once " + state); + } + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + verifyRegisteredWithRightExecutor(ctx); + ctx.fireChannelRegistered(); + } + + private void verifyRegisteredWithRightExecutor(ChannelHandlerContext ctx) { + if (ctx.channel().isRegistered() && !executor.inEventLoop()) { + throw new IllegalArgumentException("Channel handler MUST be registered with the same EventExecutor that " + + "it is created with."); + } + } + + @Override + public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { + maybeRequestMore(); + ctx.fireChannelWritabilityChanged(); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + if (state == HandlerSubscriber.State.INACTIVE) { + state = HandlerSubscriber.State.RUNNING; + maybeRequestMore(); + } + ctx.fireChannelActive(); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + cancel(); + ctx.fireChannelInactive(); + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + cancel(); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + cancel(); + ctx.fireExceptionCaught(cause); + } + + private void cancel() { + switch (state) { + case NO_SUBSCRIPTION: + state = HandlerSubscriber.State.CANCELLED; + break; + case RUNNING: + case INACTIVE: + subscription.cancel(); + state = HandlerSubscriber.State.CANCELLED; + break; + default: + // ignore + } + } + + @Override + public void onSubscribe(final Subscription subscription) { + if (subscription == null) { + throw new NullPointerException("Null subscription"); + } else if (!hasSubscription.compareAndSet(false, true)) { + subscription.cancel(); + } else { + this.subscription = subscription; + executor.execute(new Runnable() { + @Override + public void run() { + provideSubscription(); + } + }); + } + } + + private void provideSubscription() { + switch (state) { + case NO_SUBSCRIPTION_OR_CONTEXT: + state = HandlerSubscriber.State.NO_CONTEXT; + break; + case NO_SUBSCRIPTION: + maybeStart(); + break; + case CANCELLED: + subscription.cancel(); + break; + default: + // ignore + } + } + + private void maybeStart() { + if (ctx.channel().isActive()) { + state = HandlerSubscriber.State.RUNNING; + maybeRequestMore(); + } else { + state = HandlerSubscriber.State.INACTIVE; + } + } + + @Override + public void onNext(T t) { + // Publish straight to the context. + Validate.notNull(t, "Event must not be null."); + lastWriteFuture = ctx.writeAndFlush(t); + lastWriteFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + outstandingDemand--; + maybeRequestMore(); + } + }); + } + + @Override + public void onError(final Throwable error) { + if (error == null) { + throw new NullPointerException("Null error published"); + } + error(error); + } + + @Override + public void onComplete() { + if (lastWriteFuture == null) { + complete(); + } else { + lastWriteFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture channelFuture) throws Exception { + complete(); + } + }); + } + } + + private void doClose() { + executor.execute(new Runnable() { + @Override + public void run() { + switch (state) { + case NO_SUBSCRIPTION: + case INACTIVE: + case RUNNING: + ctx.close(); + state = HandlerSubscriber.State.COMPLETE; + break; + default: + // ignore + } + } + }); + } + + private void maybeRequestMore() { + if (outstandingDemand <= demandLowWatermark && ctx.channel().isWritable()) { + long toRequest = demandHighWatermark - outstandingDemand; + + outstandingDemand = demandHighWatermark; + subscription.request(toRequest); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HttpStreamsClientHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HttpStreamsClientHandler.java new file mode 100644 index 000000000000..ce24f9c565d9 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HttpStreamsClientHandler.java @@ -0,0 +1,186 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.FullHttpResponse; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpUtil; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.util.ReferenceCountUtil; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Handler that converts written {@link StreamedHttpRequest} messages into {@link HttpRequest} messages + * followed by {@link HttpContent} messages and reads {@link HttpResponse} messages followed by + * {@link HttpContent} messages and produces {@link StreamedHttpResponse} messages. + * + * This allows request and response bodies to be handled using reactive streams. + * + * There are two types of messages that this handler accepts for writing, {@link StreamedHttpRequest} and + * {@link FullHttpRequest}. Writing any other messages may potentially lead to HTTP message mangling. + * + * There are two types of messages that this handler will send down the chain, {@link StreamedHttpResponse}, + * and {@link FullHttpResponse}. If {@link io.netty.channel.ChannelOption#AUTO_READ} is false for the channel, + * then any {@link StreamedHttpResponse} messages must be subscribed to consume the body, otherwise + * it's possible that no read will be done of the messages. + * + * As long as messages are returned in the order that they arrive, this handler implicitly supports HTTP + * pipelining. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public class HttpStreamsClientHandler extends HttpStreamsHandler { + + private int inFlight = 0; + private int withServer = 0; + private ChannelPromise closeOnZeroInFlight = null; + private Subscriber awaiting100Continue; + private StreamedHttpMessage awaiting100ContinueMessage; + private boolean ignoreResponseBody = false; + + public HttpStreamsClientHandler() { + super(HttpResponse.class, HttpRequest.class); + } + + @Override + protected boolean hasBody(HttpResponse response) { + if (response.status().code() >= 100 && response.status().code() < 200) { + return false; + } + + if (response.status().equals(HttpResponseStatus.NO_CONTENT) || + response.status().equals(HttpResponseStatus.NOT_MODIFIED)) { + return false; + } + + if (HttpUtil.isTransferEncodingChunked(response)) { + return true; + } + + + if (HttpUtil.isContentLengthSet(response)) { + return HttpUtil.getContentLength(response) > 0; + } + + return true; + } + + @Override + public void close(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { + if (inFlight == 0) { + ctx.close(future); + } else { + closeOnZeroInFlight = future; + } + } + + @Override + protected void consumedInMessage(ChannelHandlerContext ctx) { + inFlight--; + withServer--; + if (inFlight == 0 && closeOnZeroInFlight != null) { + ctx.close(closeOnZeroInFlight); + } + } + + @Override + protected void receivedOutMessage(ChannelHandlerContext ctx) { + inFlight++; + } + + @Override + protected void sentOutMessage(ChannelHandlerContext ctx) { + withServer++; + } + + @Override + protected HttpResponse createEmptyMessage(HttpResponse response) { + return new EmptyHttpResponse(response); + } + + @Override + protected HttpResponse createStreamedMessage(HttpResponse response, Publisher stream) { + return new DelegateStreamedHttpResponse(response, stream); + } + + @Override + protected void subscribeSubscriberToStream(StreamedHttpMessage msg, Subscriber subscriber) { + if (HttpUtil.is100ContinueExpected(msg)) { + awaiting100Continue = subscriber; + awaiting100ContinueMessage = msg; + } else { + super.subscribeSubscriberToStream(msg, subscriber); + } + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + + if (msg instanceof HttpResponse && awaiting100Continue != null && withServer == 0) { + HttpResponse response = (HttpResponse) msg; + if (response.status().equals(HttpResponseStatus.CONTINUE)) { + super.subscribeSubscriberToStream(awaiting100ContinueMessage, awaiting100Continue); + awaiting100Continue = null; + awaiting100ContinueMessage = null; + if (msg instanceof FullHttpResponse) { + ReferenceCountUtil.release(msg); + } else { + ignoreResponseBody = true; + } + } else { + awaiting100ContinueMessage.subscribe(new CancelledSubscriber()); + awaiting100ContinueMessage = null; + awaiting100Continue.onSubscribe(new NoOpSubscription()); + awaiting100Continue.onComplete(); + awaiting100Continue = null; + super.channelRead(ctx, msg); + } + } else if (ignoreResponseBody && msg instanceof HttpContent) { + + ReferenceCountUtil.release(msg); + if (msg instanceof LastHttpContent) { + ignoreResponseBody = false; + } + } else { + super.channelRead(ctx, msg); + } + } + + private static class NoOpSubscription implements Subscription { + @Override + public void request(long n) { + } + + @Override + public void cancel() { + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HttpStreamsHandler.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HttpStreamsHandler.java new file mode 100644 index 000000000000..e2c643093cc2 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HttpStreamsHandler.java @@ -0,0 +1,391 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.handler.codec.http.FullHttpMessage; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.util.ReferenceCountUtil; +import java.util.LinkedList; +import java.util.Queue; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +abstract class HttpStreamsHandler extends ChannelDuplexHandler { + + private final Queue outgoing = new LinkedList<>(); + private final Class inClass; + private final Class outClass; + + /** + * The incoming message that is currently being streamed out to a subscriber. + * + * This is tracked so that if its subscriber cancels, we can go into a mode where we ignore the rest of the body. + * Since subscribers may cancel as many times as they like, including well after they've received all their content, + * we need to track what the current message that's being streamed out is so that we can ignore it if it's not + * currently being streamed out. + */ + private InT currentlyStreamedMessage; + + /** + * Ignore the remaining reads for the incoming message. + * + * This is used in conjunction with currentlyStreamedMessage, as well as in situations where we have received the + * full body, but still might be expecting a last http content message. + */ + private boolean ignoreBodyRead; + + /** + * Whether a LastHttpContent message needs to be written once the incoming publisher completes. + * + * Since the publisher may itself publish a LastHttpContent message, we need to track this fact, because if it + * doesn't, then we need to write one ourselves. + */ + private boolean sendLastHttpContent; + + HttpStreamsHandler(Class inClass, Class outClass) { + this.inClass = inClass; + this.outClass = outClass; + } + + /** + * Whether the given incoming message has a body. + */ + protected abstract boolean hasBody(InT in); + + /** + * Create an empty incoming message. This must be of type FullHttpMessage, and is invoked when we've determined + * that an incoming message can't have a body, so we send it on as a FullHttpMessage. + */ + protected abstract InT createEmptyMessage(InT in); + + /** + * Create a streamed incoming message with the given stream. + */ + protected abstract InT createStreamedMessage(InT in, Publisher stream); + + /** + * Invoked when an incoming message is first received. + * + * Overridden by sub classes for state tracking. + */ + protected void receivedInMessage(ChannelHandlerContext ctx) { + } + + /** + * Invoked when an incoming message is fully consumed. + * + * Overridden by sub classes for state tracking. + */ + protected void consumedInMessage(ChannelHandlerContext ctx) { + } + + /** + * Invoked when an outgoing message is first received. + * + * Overridden by sub classes for state tracking. + */ + protected void receivedOutMessage(ChannelHandlerContext ctx) { + } + + /** + * Invoked when an outgoing message is fully sent. + * + * Overridden by sub classes for state tracking. + */ + protected void sentOutMessage(ChannelHandlerContext ctx) { + } + + /** + * Subscribe the given subscriber to the given streamed message. + * + * Provided so that the client subclass can intercept this to hold off sending the body of an expect 100 continue + * request. + */ + protected void subscribeSubscriberToStream(StreamedHttpMessage msg, Subscriber subscriber) { + msg.subscribe(subscriber); + } + + /** + * Invoked every time a read of the incoming body is requested by the subscriber. + * + * Provided so that the server subclass can intercept this to send a 100 continue response. + */ + protected void bodyRequested(ChannelHandlerContext ctx) { + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception { + + if (inClass.isInstance(msg)) { + + receivedInMessage(ctx); + InT inMsg = inClass.cast(msg); + + if (inMsg instanceof FullHttpMessage) { + + // Forward as is + ctx.fireChannelRead(inMsg); + consumedInMessage(ctx); + + } else if (!hasBody(inMsg)) { + + // Wrap in empty message + ctx.fireChannelRead(createEmptyMessage(inMsg)); + consumedInMessage(ctx); + + // There will be a LastHttpContent message coming after this, ignore it + ignoreBodyRead = true; + + } else { + + currentlyStreamedMessage = inMsg; + // It has a body, stream it + HandlerPublisher publisher = new HandlerPublisher(ctx.executor(), HttpContent.class) { + @Override + protected void cancelled() { + if (ctx.executor().inEventLoop()) { + handleCancelled(ctx, inMsg); + } else { + ctx.executor().execute(new Runnable() { + @Override + public void run() { + handleCancelled(ctx, inMsg); + } + }); + } + } + + @Override + protected void requestDemand() { + bodyRequested(ctx); + super.requestDemand(); + } + }; + + ctx.channel().pipeline().addAfter(ctx.name(), ctx.name() + "-body-publisher", publisher); + ctx.fireChannelRead(createStreamedMessage(inMsg, publisher)); + } + } else if (msg instanceof HttpContent) { + handleReadHttpContent(ctx, (HttpContent) msg); + } + } + + private void handleCancelled(ChannelHandlerContext ctx, InT msg) { + if (currentlyStreamedMessage == msg) { + ignoreBodyRead = true; + // Need to do a read in case the subscriber ignored a read completed. + ctx.read(); + } + } + + private void handleReadHttpContent(ChannelHandlerContext ctx, HttpContent content) { + if (!ignoreBodyRead) { + if (content instanceof LastHttpContent) { + + if (content.content().readableBytes() > 0 || + !((LastHttpContent) content).trailingHeaders().isEmpty()) { + // It has data or trailing headers, send them + ctx.fireChannelRead(content); + } else { + ReferenceCountUtil.release(content); + } + + removeHandlerIfActive(ctx, ctx.name() + "-body-publisher"); + currentlyStreamedMessage = null; + consumedInMessage(ctx); + + } else { + ctx.fireChannelRead(content); + } + + } else { + ReferenceCountUtil.release(content); + if (content instanceof LastHttpContent) { + ignoreBodyRead = false; + if (currentlyStreamedMessage != null) { + removeHandlerIfActive(ctx, ctx.name() + "-body-publisher"); + } + currentlyStreamedMessage = null; + } + } + } + + @Override + public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { + if (ignoreBodyRead) { + ctx.read(); + } else { + ctx.fireChannelReadComplete(); + } + } + + @Override + public void write(final ChannelHandlerContext ctx, Object msg, final ChannelPromise promise) throws Exception { + if (outClass.isInstance(msg)) { + + Outgoing out = new Outgoing(outClass.cast(msg), promise); + receivedOutMessage(ctx); + + if (outgoing.isEmpty()) { + outgoing.add(out); + flushNext(ctx); + } else { + outgoing.add(out); + } + + } else if (msg instanceof LastHttpContent) { + + sendLastHttpContent = false; + ctx.write(msg, promise); + } else { + + ctx.write(msg, promise); + } + } + + protected void unbufferedWrite(final ChannelHandlerContext ctx, final Outgoing out) { + + if (out.message instanceof FullHttpMessage) { + // Forward as is + ctx.writeAndFlush(out.message, out.promise); + out.promise.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture channelFuture) throws Exception { + executeInEventLoop(ctx, new Runnable() { + @Override + public void run() { + sentOutMessage(ctx); + outgoing.remove(); + flushNext(ctx); + } + }); + } + }); + + } else if (out.message instanceof StreamedHttpMessage) { + + StreamedHttpMessage streamed = (StreamedHttpMessage) out.message; + HandlerSubscriber subscriber = new HandlerSubscriber(ctx.executor()) { + @Override + protected void error(Throwable error) { + out.promise.tryFailure(error); + ctx.close(); + } + + @Override + protected void complete() { + executeInEventLoop(ctx, new Runnable() { + @Override + public void run() { + completeBody(ctx); + } + }); + } + }; + + sendLastHttpContent = true; + + // DON'T pass the promise through, create a new promise instead. + ctx.writeAndFlush(out.message); + + ctx.pipeline().addAfter(ctx.name(), ctx.name() + "-body-subscriber", subscriber); + subscribeSubscriberToStream(streamed, subscriber); + } + + } + + private void completeBody(final ChannelHandlerContext ctx) { + removeHandlerIfActive(ctx, ctx.name() + "-body-subscriber"); + + if (sendLastHttpContent) { + ChannelPromise promise = outgoing.peek().promise; + ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT, promise).addListener( + new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture channelFuture) throws Exception { + executeInEventLoop(ctx, new Runnable() { + @Override + public void run() { + outgoing.remove(); + sentOutMessage(ctx); + flushNext(ctx); + } + }); + } + } + ); + } else { + outgoing.remove().promise.setSuccess(); + sentOutMessage(ctx); + flushNext(ctx); + } + } + + /** + * Most operations we want to do even if the channel is not active, because if it's not, then we want to encounter + * the error that occurs when that operation happens and so that it can be passed up to the user. However, removing + * handlers should only be done if the channel is active, because the error that is encountered when they aren't + * makes no sense to the user (NoSuchElementException). + */ + private void removeHandlerIfActive(ChannelHandlerContext ctx, String name) { + if (ctx.channel().isActive()) { + ctx.pipeline().remove(name); + } + } + + private void flushNext(ChannelHandlerContext ctx) { + if (!outgoing.isEmpty()) { + unbufferedWrite(ctx, outgoing.element()); + } else { + ctx.fireChannelWritabilityChanged(); + } + } + + private void executeInEventLoop(ChannelHandlerContext ctx, Runnable runnable) { + if (ctx.executor().inEventLoop()) { + runnable.run(); + } else { + ctx.executor().execute(runnable); + } + } + + class Outgoing { + final OutT message; + final ChannelPromise promise; + + Outgoing(OutT message, ChannelPromise promise) { + this.message = message; + this.promise = promise; + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpMessage.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpMessage.java new file mode 100644 index 000000000000..23230e3c67fa --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpMessage.java @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMessage; +import org.reactivestreams.Publisher; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Combines {@link HttpMessage} and {@link Publisher} into one + * message. So it represents an http message with a stream of {@link HttpContent} + * messages that can be subscribed to. + * + * Note that receivers of this message must consume the publisher, + * since the publisher will exert back pressure up the stream if not consumed. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public interface StreamedHttpMessage extends HttpMessage, Publisher { +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpRequest.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpRequest.java new file mode 100644 index 000000000000..750715ead4ef --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpRequest.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpRequest; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Combines {@link HttpRequest} and {@link StreamedHttpMessage} into one + * message. So it represents an http request with a stream of + * {@link io.netty.handler.codec.http.HttpContent} messages that can be subscribed to. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public interface StreamedHttpRequest extends HttpRequest, StreamedHttpMessage { +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpResponse.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpResponse.java new file mode 100644 index 000000000000..6ce4aa900375 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/StreamedHttpResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.handler.codec.http.HttpResponse; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Combines {@link HttpResponse} and {@link StreamedHttpMessage} into one + * message. So it represents an http response with a stream of + * {@link io.netty.handler.codec.http.HttpContent} messages that can be subscribed to. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +@SdkInternalApi +public interface StreamedHttpResponse extends HttpResponse, StreamedHttpMessage { +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/package-info.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/package-info.java new file mode 100644 index 000000000000..ba9b003b0d3c --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/nrs/package-info.java @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * This package contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +package software.amazon.awssdk.http.nio.netty.internal.nrs; diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/BetterFixedChannelPool.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/BetterFixedChannelPool.java index 50a0d3c23e82..b22f2353a14e 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/BetterFixedChannelPool.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/BetterFixedChannelPool.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -70,7 +70,7 @@ public enum AcquireTimeoutAction { // There is no need to worry about synchronization as everything that modified the queue or counts is done // by the above EventExecutor. - private final Queue pendingAcquireQueue = new ArrayDeque(); + private final Queue pendingAcquireQueue = new ArrayDeque<>(); private final int maxConnections; private final int maxPendingAcquires; private int acquiredChannelCount; @@ -137,12 +137,7 @@ public Future acquire(final Promise promise) { if (executor.inEventLoop()) { acquire0(promise); } else { - executor.execute(new Runnable() { - @Override - public void run() { - acquire0(promise); - } - }); + executor.execute(() -> acquire0(promise)); } } catch (Throwable cause) { promise.setFailure(cause); @@ -348,12 +343,7 @@ public void close() { if (executor.inEventLoop()) { close0(); } else { - executor.submit(new Runnable() { - @Override - public void run() { - close0(); - } - }).awaitUninterruptibly(); + executor.submit(() -> close0()).awaitUninterruptibly(); } } @@ -376,12 +366,7 @@ private void close0() { // Ensure we dispatch this on another Thread as close0 will be called from the EventExecutor and we need // to ensure we will not block in a EventExecutor. - GlobalEventExecutor.INSTANCE.execute(new Runnable() { - @Override - public void run() { - delegateChannelPool.close(); - } - }); + GlobalEventExecutor.INSTANCE.execute(() -> delegateChannelPool.close()); } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtils.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtils.java index 2b437e70630e..0901fa8e0590 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtils.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -26,7 +26,8 @@ @SdkInternalApi public final class ChannelUtils { - private ChannelUtils() {} + private ChannelUtils() { + } /** * Removes handlers of the given class types from the pipeline. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/DelegatingChannelHandlerContext.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/DelegatingChannelHandlerContext.java new file mode 100644 index 000000000000..d1c5f43de607 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/DelegatingChannelHandlerContext.java @@ -0,0 +1,248 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.utils; + +import io.netty.buffer.ByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelProgressivePromise; +import io.netty.channel.ChannelPromise; +import io.netty.util.Attribute; +import io.netty.util.AttributeKey; +import io.netty.util.concurrent.EventExecutor; +import java.net.SocketAddress; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * An abstract implementation of {@link ChannelHandlerContext} that delegates to another + * context for non-overridden methods. + */ +@SdkInternalApi +public abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext { + private final ChannelHandlerContext delegate; + + public DelegatingChannelHandlerContext(ChannelHandlerContext delegate) { + this.delegate = delegate; + } + + @Override + public Channel channel() { + return delegate.channel(); + } + + @Override + public EventExecutor executor() { + return delegate.executor(); + } + + @Override + public String name() { + return delegate.name(); + } + + @Override + public ChannelHandler handler() { + return delegate.handler(); + } + + @Override + public boolean isRemoved() { + return delegate.isRemoved(); + } + + @Override + public ChannelHandlerContext fireChannelRegistered() { + return delegate.fireChannelRegistered(); + } + + @Override + public ChannelHandlerContext fireChannelUnregistered() { + return delegate.fireChannelUnregistered(); + } + + @Override + public ChannelHandlerContext fireChannelActive() { + return delegate.fireChannelActive(); + } + + @Override + public ChannelHandlerContext fireChannelInactive() { + return delegate.fireChannelInactive(); + } + + @Override + public ChannelHandlerContext fireExceptionCaught(Throwable cause) { + return delegate.fireExceptionCaught(cause); + } + + @Override + public ChannelHandlerContext fireUserEventTriggered(Object evt) { + return delegate.fireUserEventTriggered(evt); + } + + @Override + public ChannelHandlerContext fireChannelRead(Object msg) { + return delegate.fireChannelRead(msg); + } + + @Override + public ChannelHandlerContext fireChannelReadComplete() { + return delegate.fireChannelReadComplete(); + } + + @Override + public ChannelHandlerContext fireChannelWritabilityChanged() { + return delegate.fireChannelWritabilityChanged(); + } + + @Override + public ChannelFuture bind(SocketAddress localAddress) { + return delegate.bind(localAddress); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress) { + return delegate.connect(remoteAddress); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { + return delegate.connect(remoteAddress, localAddress); + } + + @Override + public ChannelFuture disconnect() { + return delegate.disconnect(); + } + + @Override + public ChannelFuture close() { + return delegate.close(); + } + + @Override + public ChannelFuture deregister() { + return delegate.deregister(); + } + + @Override + public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { + return delegate.bind(localAddress, promise); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { + return delegate.connect(remoteAddress, promise); + } + + @Override + public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + return delegate.connect(remoteAddress, localAddress, promise); + } + + @Override + public ChannelFuture disconnect(ChannelPromise promise) { + return delegate.disconnect(promise); + } + + @Override + public ChannelFuture close(ChannelPromise promise) { + return delegate.close(promise); + } + + @Override + public ChannelFuture deregister(ChannelPromise promise) { + return delegate.deregister(promise); + } + + @Override + public ChannelHandlerContext read() { + return delegate.read(); + } + + @Override + public ChannelFuture write(Object msg) { + return delegate.write(msg); + } + + @Override + public ChannelFuture write(Object msg, ChannelPromise promise) { + return delegate.write(msg, promise); + } + + @Override + public ChannelHandlerContext flush() { + return delegate.flush(); + } + + @Override + public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { + return delegate.writeAndFlush(msg, promise); + } + + @Override + public ChannelFuture writeAndFlush(Object msg) { + return delegate.writeAndFlush(msg); + } + + @Override + public ChannelPromise newPromise() { + return delegate.newPromise(); + } + + @Override + public ChannelProgressivePromise newProgressivePromise() { + return delegate.newProgressivePromise(); + } + + @Override + public ChannelFuture newSucceededFuture() { + return delegate.newSucceededFuture(); + } + + @Override + public ChannelFuture newFailedFuture(Throwable cause) { + return delegate.newFailedFuture(cause); + } + + @Override + public ChannelPromise voidPromise() { + return delegate.voidPromise(); + } + + @Override + public ChannelPipeline pipeline() { + return delegate.pipeline(); + } + + @Override + public ByteBufAllocator alloc() { + return delegate.alloc(); + } + + @Override + public Attribute attr(AttributeKey key) { + return delegate.attr(key); + } + + @Override + public boolean hasAttr(AttributeKey key) { + return delegate.hasAttr(key); + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtils.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtils.java index 74d53b02a338..edf117884672 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtils.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -22,7 +22,8 @@ @SdkInternalApi public final class ExceptionHandlingUtils { - private ExceptionHandlingUtils() {} + private ExceptionHandlingUtils() { + } /** * Runs a task within try-catch block. All exceptions thrown from the execution diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java index 6fa8842ec503..4d0b20b4a128 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ package software.amazon.awssdk.http.nio.netty.internal.utils; +import io.netty.channel.EventLoop; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; @@ -24,15 +25,17 @@ import java.util.function.BiConsumer; import java.util.function.Function; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Logger; @SdkInternalApi public final class NettyUtils { - /** * Completed succeed future. */ public static final SucceededFuture SUCCEEDED_FUTURE = new SucceededFuture<>(null, null); + private static final Logger log = Logger.loggerFor(NettyUtils.class); + private NettyUtils() { } @@ -55,7 +58,7 @@ private NettyUtils() { } else { try { promise.setSuccess(successFunction.apply(success)); - } catch (Exception e) { + } catch (Throwable e) { promise.setFailure(e); } } @@ -82,7 +85,7 @@ private NettyUtils() { } else { try { successConsumer.accept(success, promise); - } catch (Exception e) { + } catch (Throwable e) { // If the successConsumer fails synchronously then we can notify the promise. If it fails asynchronously // it's up to the successConsumer to notify. promise.setFailure(e); @@ -132,9 +135,29 @@ public static void doInEventLoop(EventExecutor eventExecutor, Runnable runnable) */ public static void doInEventLoop(EventExecutor eventExecutor, Runnable runnable, Promise promise) { try { - doInEventLoop(eventExecutor, runnable); - } catch (Exception e) { + if (eventExecutor.inEventLoop()) { + runnable.run(); + } else { + eventExecutor.submit(() -> { + try { + runnable.run(); + } catch (Throwable e) { + promise.setFailure(e); + } + }); + } + } catch (Throwable e) { promise.setFailure(e); } } + + public static void warnIfNotInEventLoop(EventLoop loop) { + assert loop.inEventLoop(); + if (!loop.inEventLoop()) { + Exception exception = + new IllegalStateException("Execution is not in the expected event loop. Please report this issue to the " + + "AWS SDK for Java team on GitHub, because it could result in race conditions."); + log.warn(() -> "Execution is happening outside of the expected event loop.", exception); + } + } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/OrderedWriteChannelHandlerContext.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/OrderedWriteChannelHandlerContext.java new file mode 100644 index 000000000000..9e06a565e8e2 --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/OrderedWriteChannelHandlerContext.java @@ -0,0 +1,91 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.utils; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.util.AttributeKey; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * An implementation of {@link ChannelHandlerContext} that ensures all writes are performed in the order they are invoked. + * + * This works around https://github.com/netty/netty/issues/7783 where writes by an event loop 'skip ahead' of writes off of the + * event loop. + */ +@SdkInternalApi +public class OrderedWriteChannelHandlerContext extends DelegatingChannelHandlerContext { + private static final AttributeKey ORDERED = + AttributeKey.newInstance("aws.http.nio.netty.async.OrderedWriteChannelHandlerContext.ORDERED"); + + private OrderedWriteChannelHandlerContext(ChannelHandlerContext delegate) { + super(delegate); + delegate.channel().attr(ORDERED).set(null); + } + + public static ChannelHandlerContext wrap(ChannelHandlerContext ctx) { + if (ctx.channel().hasAttr(ORDERED)) { + return ctx; + } + return new OrderedWriteChannelHandlerContext(ctx); + } + + @Override + public ChannelFuture write(Object msg) { + return doInOrder(promise -> super.write(msg, promise)); + } + + @Override + public ChannelFuture write(Object msg, ChannelPromise promise) { + doInOrder(() -> super.write(msg, promise)); + return promise; + } + + @Override + public ChannelFuture writeAndFlush(Object msg) { + return doInOrder(promise -> super.writeAndFlush(msg, promise)); + } + + @Override + public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { + doInOrder(() -> super.writeAndFlush(msg, promise)); + return promise; + } + + private ChannelFuture doInOrder(Consumer task) { + ChannelPromise promise = newPromise(); + if (!channel().eventLoop().inEventLoop()) { + task.accept(promise); + } else { + // If we're in the event loop, queue a task to perform the write, so that it occurs after writes that were scheduled + // off of the event loop. + channel().eventLoop().execute(() -> task.accept(promise)); + } + return promise; + } + + private void doInOrder(Runnable task) { + if (!channel().eventLoop().inEventLoop()) { + task.run(); + } else { + // If we're in the event loop, queue a task to perform the write, so that it occurs after writes that were scheduled + // off of the event loop. + channel().eventLoop().execute(task); + } + } +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java index e1302fe2f74e..1d80dad5850f 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/main/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService b/http-clients/netty-nio-client/src/main/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService index 22e47f11f670..c3e05ecd129a 100644 --- a/http-clients/netty-nio-client/src/main/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService +++ b/http-clients/netty-nio-client/src/main/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ClientTlsAuthTestBase.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ClientTlsAuthTestBase.java index eb8946970db4..468aad992e47 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ClientTlsAuthTestBase.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ClientTlsAuthTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/EmptyPublisher.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/EmptyPublisher.java index 78af13d3869b..1f1308a2f07f 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/EmptyPublisher.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/EmptyPublisher.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty; import java.nio.ByteBuffer; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/Http2ConfigurationTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/Http2ConfigurationTest.java new file mode 100644 index 000000000000..ec93d1455fb5 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/Http2ConfigurationTest.java @@ -0,0 +1,91 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty; + +import static org.assertj.core.api.Assertions.assertThat; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class Http2ConfigurationTest { + @Rule + public ExpectedException expected = ExpectedException.none(); + + @Test + public void builder_returnsInstance() { + assertThat(Http2Configuration.builder()).isNotNull(); + } + + @Test + public void build_buildsCorrectConfig() { + long maxStreams = 1; + int initialWindowSize = 2; + + Http2Configuration config = Http2Configuration.builder() + .maxStreams(maxStreams) + .initialWindowSize(initialWindowSize) + .build(); + + assertThat(config.maxStreams()).isEqualTo(maxStreams); + assertThat(config.initialWindowSize()).isEqualTo(initialWindowSize); + } + + @Test + public void builder_toBuilder_roundTrip() { + Http2Configuration config1 = Http2Configuration.builder() + .maxStreams(7L) + .initialWindowSize(42) + .build(); + + Http2Configuration config2 = config1.toBuilder().build(); + + assertThat(config1).isEqualTo(config2); + } + + @Test + public void builder_maxStream_nullValue_doesNotThrow() { + Http2Configuration.builder().maxStreams(null); + } + + @Test + public void builder_maxStream_negative_throws() { + expected.expect(IllegalArgumentException.class); + Http2Configuration.builder().maxStreams(-1L); + } + + @Test + public void builder_maxStream_0_throws() { + expected.expect(IllegalArgumentException.class); + Http2Configuration.builder().maxStreams(0L); + } + + @Test + public void builder_initialWindowSize_nullValue_doesNotThrow() { + Http2Configuration.builder().initialWindowSize(null); + } + + @Test + public void builder_initialWindowSize_negative_throws() { + expected.expect(IllegalArgumentException.class); + Http2Configuration.builder().initialWindowSize(-1); + } + + @Test + public void builder_initialWindowSize_0_throws() { + expected.expect(IllegalArgumentException.class); + Http2Configuration.builder().initialWindowSize(0); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java index aabb31a54dea..b9533c8527cd 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientSpiVerificationTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientSpiVerificationTest.java index 870e1b5f1931..9b992cc90918 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientSpiVerificationTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientSpiVerificationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java index 07951a02e562..23a887321616 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; +import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.http.Fault; import com.github.tomakehurst.wiremock.junit.WireMockRule; import io.netty.channel.Channel; @@ -68,6 +69,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Stream; +import javax.net.ssl.TrustManagerFactory; import org.assertj.core.api.Condition; import org.junit.AfterClass; import org.junit.Before; @@ -79,6 +81,7 @@ import org.mockito.stubbing.Answer; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; +import software.amazon.awssdk.http.HttpTestUtils; import software.amazon.awssdk.http.SdkHttpConfigurationOption; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; @@ -113,6 +116,32 @@ public static void tearDown() throws Exception { client.close(); } + @Test + public void defaultConnectionIdleTimeout() { + try (NettyNioAsyncHttpClient client = (NettyNioAsyncHttpClient) NettyNioAsyncHttpClient.builder().build()) { + assertThat(client.configuration().idleTimeoutMillis()).isEqualTo(5000); + } + } + + @Test + public void overrideConnectionIdleTimeout_shouldHonor() { + try (NettyNioAsyncHttpClient client = (NettyNioAsyncHttpClient) NettyNioAsyncHttpClient.builder() + .connectionMaxIdleTime(Duration.ofMillis(1000)) + .build()) { + assertThat(client.configuration().idleTimeoutMillis()).isEqualTo(1000); + } + } + + @Test + public void invalidMaxPendingConnectionAcquireConfig_shouldPropagateException() { + try (SdkAsyncHttpClient customClient = NettyNioAsyncHttpClient.builder() + .maxConcurrency(1) + .maxPendingConnectionAcquires(0) + .build()) { + assertThatThrownBy(() -> makeSimpleRequest(customClient)).hasMessageContaining("java.lang.IllegalArgumentException: maxPendingAcquires: 0 (expected: >= 1)"); + } + } + @Test public void customFactoryIsUsed() throws Exception { ThreadFactory threadFactory = spy(new CustomThreadFactory()); @@ -349,6 +378,30 @@ public void responseConnectionClosed_shouldCloseAndReleaseChannel() throws Excep eventLoopGroup.eventLoopGroup().shutdownGracefully().awaitUninterruptibly(); } + @Test + public void builderUsesProvidedTrustManagersProvider() throws Exception { + WireMockServer selfSignedServer = HttpTestUtils.createSelfSignedServer(); + + TrustManagerFactory managerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + managerFactory.init(HttpTestUtils.getSelfSignedKeyStore()); + + try (SdkAsyncHttpClient netty = NettyNioAsyncHttpClient.builder() + .tlsTrustManagersProvider(managerFactory::getTrustManagers) + .build()) { + selfSignedServer.start(); + URI uri = URI.create("https://localhost:" + selfSignedServer.httpsPort()); + + SdkHttpRequest request = createRequest(uri); + RecordingResponseHandler recorder = new RecordingResponseHandler(); + client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider("")).responseHandler(recorder).build()); + + recorder.completeFuture.get(5, TimeUnit.SECONDS); + } finally { + selfSignedServer.stop(); + } + } + + /** * Make a simple async request and wait for it to fiish. * diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyConfigurationTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyConfigurationTest.java index 753a2f4b6ac4..239754f22e21 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyConfigurationTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java index afab5f09a218..9a0b45094eec 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingNetworkTrafficListener.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingNetworkTrafficListener.java index a9b3ba99e15c..d9ed5cad9f07 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingNetworkTrafficListener.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingNetworkTrafficListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingResponseHandler.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingResponseHandler.java index 483fce008ecd..52989a2bb966 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingResponseHandler.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/RecordingResponseHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java index 61ea7420ad38..a3ae76469359 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/GoAwayTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/GoAwayTest.java new file mode 100644 index 000000000000..f46480dc29a2 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/GoAwayTest.java @@ -0,0 +1,450 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.fault; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.DefaultHttp2FrameReader; +import io.netty.handler.codec.http2.DefaultHttp2FrameWriter; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.Http2FrameAdapter; +import io.netty.handler.codec.http2.Http2FrameListener; +import io.netty.handler.codec.http2.Http2FrameReader; +import io.netty.handler.codec.http2.Http2FrameWriter; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.util.AttributeKey; +import io.reactivex.Flowable; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; +import org.junit.After; +import org.junit.Test; +import org.reactivestreams.Publisher; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; +import software.amazon.awssdk.http.nio.netty.EmptyPublisher; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; +import software.amazon.awssdk.http.nio.netty.internal.http2.GoAwayException; + +/** + * Tests to ensure that the client behaves as expected when it receives GOAWAY messages. + */ +public class GoAwayTest { + + private SdkAsyncHttpClient netty; + private SimpleEndpointDriver endpointDriver; + + @After + public void teardown() throws InterruptedException { + if (endpointDriver != null) { + endpointDriver.shutdown(); + } + endpointDriver = null; + + if (netty != null) { + netty.close(); + } + netty = null; + } + + @Test + public void goAwayCanCloseAllStreams() throws InterruptedException { + Set serverChannels = ConcurrentHashMap.newKeySet(); + + CountDownLatch allRequestsReceived = new CountDownLatch(2); + Supplier frameListenerSupplier = () -> new TestFrameListener() { + @Override + public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endStream) { + onHeadersReadDelegator(ctx, streamId); + } + + @Override + public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { + onHeadersReadDelegator(ctx, streamId); + } + + private void onHeadersReadDelegator(ChannelHandlerContext ctx, int streamId) { + serverChannels.add(ctx.channel().id().asShortText()); + + Http2Headers outboundHeaders = new DefaultHttp2Headers() + .status("200") + .add("content-type", "text/plain") + .addInt("content-length", 5); + + frameWriter().writeHeaders(ctx, streamId, outboundHeaders, 0, false, ctx.newPromise()); + ctx.flush(); + + allRequestsReceived.countDown(); + } + }; + + endpointDriver = new SimpleEndpointDriver(frameListenerSupplier); + endpointDriver.init(); + + netty = NettyNioAsyncHttpClient.builder() + .protocol(Protocol.HTTP2) + .build(); + + CompletableFuture request1 = sendGetRequest(); + CompletableFuture request2 = sendGetRequest(); + + allRequestsReceived.await(); + + endpointDriver.channels.forEach(ch -> { + if (serverChannels.contains(ch.id().asShortText())) { + endpointDriver.goAway(ch, 0); + } + }); + + assertThatThrownBy(() -> request1.join()) + .hasMessageContaining("GOAWAY received from service") + .hasCauseInstanceOf(GoAwayException.class); + + assertThatThrownBy(() -> request2.join()) + .hasMessageContaining("GOAWAY received from service") + .hasCauseInstanceOf(GoAwayException.class); + + assertThat(endpointDriver.currentConnectionCount.get()).isEqualTo(0); + } + + @Test + public void execute_goAwayReceived_existingChannelsNotReused() throws InterruptedException { + // Frame listener supplier for each connection + Supplier frameListenerSupplier = () -> new TestFrameListener() { + @Override + public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endStream) { + onHeadersReadDelegator(ctx, streamId); + } + + @Override + public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { + onHeadersReadDelegator(ctx, streamId); + } + + private void onHeadersReadDelegator(ChannelHandlerContext ctx, int streamId) { + frameWriter().writeHeaders(ctx, streamId, new DefaultHttp2Headers().add("content-length", "0").status("204"), 0, true, ctx.newPromise()); + ctx.flush(); + } + }; + + endpointDriver = new SimpleEndpointDriver(frameListenerSupplier); + endpointDriver.init(); + + netty = NettyNioAsyncHttpClient.builder() + .eventLoopGroup(SdkEventLoopGroup.builder().numberOfThreads(1).build()) + .protocol(Protocol.HTTP2) + .build(); + + sendGetRequest().join(); + + // Note: It's possible the initial request can cause the client to allocate more than 1 channel + int initialChannelNum = endpointDriver.channels.size(); + + // Send GOAWAY to all the currently open channels + endpointDriver.channels.forEach(ch -> endpointDriver.goAway(ch, 1)); + + // Need to give a chance for the streams to get closed + Thread.sleep(1000); + + // Since the existing channels are now invalid, this request should cause a new channel to be opened + sendGetRequest().join(); + + assertThat(endpointDriver.channels).hasSize(initialChannelNum + 1); + } + + // The client should not close streams that are less than the 'last stream + // ID' given in the GOAWAY frame since it means they were processed fully + @Test + public void execute_goAwayReceived_lastStreamId_lowerStreamsNotClosed() throws InterruptedException { + ConcurrentHashMap> channelToStreams = new ConcurrentHashMap<>(); + + CompletableFuture stream3Received = new CompletableFuture<>(); + CountDownLatch allRequestsReceived = new CountDownLatch(2); + byte[] getPayload = "go away!".getBytes(StandardCharsets.UTF_8); + Supplier frameListenerSupplier = () -> new TestFrameListener() { + @Override + public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endStream) { + onHeadersReadDelegator(ctx, streamId); + } + + @Override + public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { + onHeadersReadDelegator(ctx, streamId); + } + + private void onHeadersReadDelegator(ChannelHandlerContext ctx, int streamId) { + channelToStreams.computeIfAbsent(ctx.channel().id().asShortText(), (k) -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(streamId); + + if (streamId == 3) { + stream3Received.complete(null); + } + + if (streamId < 5) { + Http2Headers outboundHeaders = new DefaultHttp2Headers() + .status("200") + .add("content-type", "text/plain") + .addInt("content-length", getPayload.length); + + frameWriter().writeHeaders(ctx, streamId, outboundHeaders, 0, false, ctx.newPromise()); + ctx.flush(); + } + + allRequestsReceived.countDown(); + } + }; + + endpointDriver = new SimpleEndpointDriver(frameListenerSupplier); + endpointDriver.init(); + + netty = NettyNioAsyncHttpClient.builder() + .protocol(Protocol.HTTP2) + .build(); + + CompletableFuture stream3Cf = sendGetRequest();// stream ID 3 + + // Wait for the request to be received just to ensure that it is given ID 3 + stream3Received.join(); + + CompletableFuture stream5Cf = sendGetRequest();// stream ID 5 + + allRequestsReceived.await(10, TimeUnit.SECONDS); + + // send the GOAWAY first, specifying that everything after 3 is not processed + endpointDriver.channels.forEach(ch -> { + Set streams = channelToStreams.getOrDefault(ch.id().asShortText(), Collections.emptySet()); + if (streams.contains(3)) { + endpointDriver.goAway(ch, 3); + } + }); + + // now send the DATA for stream 3, which should still be valid + endpointDriver.channels.forEach(ch -> { + Set streams = channelToStreams.getOrDefault(ch.id().asShortText(), Collections.emptySet()); + if (streams.contains(3)) { + endpointDriver.data(ch, 3, getPayload); + } + }); + + waitForFuture(stream3Cf); + waitForFuture(stream5Cf); + + assertThat(stream3Cf.isCompletedExceptionally()).isFalse(); + assertThat(stream5Cf.isCompletedExceptionally()).isTrue(); + stream5Cf.exceptionally(e -> { + assertThat(e).isInstanceOf(IOException.class); + return null; + }); + } + + private CompletableFuture sendGetRequest() { + AsyncExecuteRequest req = AsyncExecuteRequest.builder() + .responseHandler(new SdkAsyncHttpResponseHandler() { + private SdkHttpResponse headers; + + @Override + public void onHeaders(SdkHttpResponse headers) { + this.headers = headers; + } + + @Override + public void onStream(Publisher stream) { + // Consume the stream in order to complete request + Flowable.fromPublisher(stream).subscribe(b -> {}, t -> {}); + } + + @Override + public void onError(Throwable error) { + } + }) + .request(SdkHttpFullRequest.builder() + .method(SdkHttpMethod.GET) + .protocol("http") + .host("localhost") + .port(endpointDriver.port()) + .build()) + .requestContentPublisher(new EmptyPublisher()) + .build(); + + return netty.execute(req); + } + + private static void waitForFuture(CompletableFuture cf) { + try { + cf.get(2, TimeUnit.SECONDS); + } catch (ExecutionException | InterruptedException t) { + } catch (TimeoutException t) { + throw new RuntimeException("Future did not complete after 2 seconds.", t); + } + } + + // Minimal class to simulate an H2 endpoint + private static class SimpleEndpointDriver extends ChannelInitializer { + private List channels = new ArrayList<>(); + private final NioEventLoopGroup group = new NioEventLoopGroup(); + private final Supplier frameListenerSupplier; + private ServerBootstrap bootstrap; + private ServerSocketChannel serverSock; + private AtomicInteger currentConnectionCount = new AtomicInteger(0); + + public SimpleEndpointDriver(Supplier frameListenerSupplier) { + this.frameListenerSupplier = frameListenerSupplier; + } + + public void init() throws InterruptedException { + bootstrap = new ServerBootstrap() + .channel(NioServerSocketChannel.class) + .group(new NioEventLoopGroup()) + .childHandler(this) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + serverSock = (ServerSocketChannel) bootstrap.bind(0).sync().channel(); + } + + public void shutdown() throws InterruptedException { + group.shutdownGracefully().await(); + } + + public int port() { + return serverSock.localAddress().getPort(); + } + + public void goAway(SocketChannel ch, int lastStreamId) { + ByteBuf b = ch.alloc().buffer(9 + 8); + + // Frame header + b.writeMedium(8); // Payload length + b.writeByte(0x7); // Type = GOAWAY + b.writeByte(0x0); // Flags + b.writeInt(0); // 0 = connection frame + + // GOAWAY payload + b.writeInt(lastStreamId); + b.writeInt(0); // Error code + + ch.writeAndFlush(b); + } + + public void data(SocketChannel ch, int streamId, byte[] payload) { + ByteBuf b = ch.alloc().buffer(9 + payload.length); + + // Header + b.writeMedium(payload.length); // Payload length + b.writeByte(0); // Type = DATA + b.writeByte(0x1); // 0x1 = EOF + b.writeInt(streamId); + + // Payload + b.writeBytes(payload); + + ch.writeAndFlush(b); + } + + @Override + protected void initChannel(SocketChannel ch) throws Exception { + channels.add(ch); + ch.pipeline().addLast(new Http2ConnHandler(this, frameListenerSupplier.get())); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + currentConnectionCount.incrementAndGet(); + super.channelActive(ctx); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + currentConnectionCount.decrementAndGet(); + super.channelInactive(ctx); + } + } + + private abstract class TestFrameListener extends Http2FrameAdapter { + private final Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); + + protected final Http2FrameWriter frameWriter() { + return frameWriter; + } + + @Override + public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) { + frameWriter().writeSettings(ctx, new Http2Settings(), ctx.newPromise()); + frameWriter().writeSettingsAck(ctx, ctx.newPromise()); + ctx.flush(); + } + } + + private static class Http2ConnHandler extends ChannelDuplexHandler { + // Prior knowledge preface + private static final String PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + private static final AttributeKey H2_ESTABLISHED = AttributeKey.newInstance("h2-etablished"); + + private final Http2FrameReader frameReader = new DefaultHttp2FrameReader(); + private final SimpleEndpointDriver simpleEndpointDriver; + private final Http2FrameListener frameListener; + + public Http2ConnHandler(SimpleEndpointDriver simpleEndpointDriver, Http2FrameListener frameListener) { + this.simpleEndpointDriver = simpleEndpointDriver; + this.frameListener = frameListener; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ByteBuf bb = (ByteBuf) msg; + if (!isH2Established(ctx.channel())) { + String prefaceString = bb.readCharSequence(24, StandardCharsets.UTF_8).toString(); + if (PREFACE.equals(prefaceString)) { + ctx.channel().attr(H2_ESTABLISHED).set(true); + } + } + frameReader.readFrame(ctx, bb, frameListener); + } + + private boolean isH2Established(Channel ch) { + return Boolean.TRUE.equals(ch.attr(H2_ESTABLISHED).get()); + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/PingTimeoutTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/PingTimeoutTest.java new file mode 100644 index 000000000000..a309addf27ff --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/PingTimeoutTest.java @@ -0,0 +1,257 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.fault; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Frame; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2FrameLogger; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.time.Instant; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; +import software.amazon.awssdk.http.nio.netty.EmptyPublisher; +import software.amazon.awssdk.http.nio.netty.Http2Configuration; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.internal.http2.PingFailedException; + +/** + * Testing the scenario where the server never acks PING + */ +public class PingTimeoutTest { + @Rule + public ExpectedException expected = ExpectedException.none(); + + private Server server; + private SdkAsyncHttpClient netty; + + @Before + public void methodSetup() throws Exception { + server = new Server(); + server.init(); + } + + @After + public void methodTeardown() throws InterruptedException { + server.shutdown(); + + if (netty != null) { + netty.close(); + } + + netty = null; + } + + @Test + public void pingHealthCheck_null_shouldThrowExceptionAfter5Sec() { + Instant a = Instant.now(); + assertThatThrownBy(() -> makeRequest(null).join()) + .hasMessageContaining("An error occurred on the connection") + .hasCauseInstanceOf(IOException.class) + .hasRootCauseInstanceOf(PingFailedException.class); + assertThat(Duration.between(a, Instant.now())).isBetween(Duration.ofSeconds(5), Duration.ofSeconds(7)); + } + + @Test + public void pingHealthCheck_10sec_shouldThrowExceptionAfter10Secs() { + Instant a = Instant.now(); + assertThatThrownBy(() -> makeRequest(Duration.ofSeconds(10)).join()).hasCauseInstanceOf(IOException.class) + .hasMessageContaining("An error occurred on the connection") + .hasRootCauseInstanceOf(PingFailedException.class); + assertThat(Duration.between(a, Instant.now())).isBetween(Duration.ofSeconds(10), Duration.ofSeconds(12)); + } + + @Test + public void pingHealthCheck_0_disabled_shouldNotThrowException() throws Exception { + expected.expect(TimeoutException.class); + CompletableFuture requestFuture = makeRequest(Duration.ofMillis(0)); + try { + requestFuture.get(8, TimeUnit.SECONDS); + } finally { + assertThat(requestFuture.isDone()).isFalse(); + } + } + + private CompletableFuture makeRequest(Duration healthCheckPingPeriod) { + netty = NettyNioAsyncHttpClient.builder() + .protocol(Protocol.HTTP2) + .http2Configuration(Http2Configuration.builder().healthCheckPingPeriod(healthCheckPingPeriod).build()) + .build(); + + SdkHttpFullRequest request = SdkHttpFullRequest.builder() + .protocol("http") + .host("localhost") + .port(server.port()) + .method(SdkHttpMethod.GET) + .build(); + + AsyncExecuteRequest executeRequest = AsyncExecuteRequest.builder() + .fullDuplex(false) + .request(request) + .requestContentPublisher(new EmptyPublisher()) + .responseHandler(new SdkAsyncHttpResponseHandler() { + @Override + public void onHeaders(SdkHttpResponse headers) { + } + + @Override + public void onStream(Publisher stream) { + stream.subscribe(new Subscriber() { + @Override + public void onSubscribe(Subscription s) { + s.request(Integer.MAX_VALUE); + } + + @Override + public void onNext(ByteBuffer byteBuffer) { + } + + @Override + public void onError(Throwable t) { + } + + @Override + public void onComplete() { + } + }); + } + + @Override + public void onError(Throwable error) { + } + }) + .build(); + + return netty.execute(executeRequest); + } + + private static class Server extends ChannelInitializer { + private ServerBootstrap bootstrap; + private ServerSocketChannel serverSock; + private String[] channelIds = new String[5]; + private final NioEventLoopGroup group = new NioEventLoopGroup(); + private SslContext sslCtx; + private AtomicInteger h2ConnectionCount = new AtomicInteger(0); + + void init() throws Exception { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + bootstrap = new ServerBootstrap() + .channel(NioServerSocketChannel.class) + .group(group) + .childHandler(this); + + serverSock = (ServerSocketChannel) bootstrap.bind(0).sync().channel(); + } + + @Override + protected void initChannel(Channel ch) { + channelIds[h2ConnectionCount.get()] = ch.id().asShortText(); + ch.pipeline().addFirst(new LoggingHandler(LogLevel.DEBUG)); + h2ConnectionCount.incrementAndGet(); + + ChannelPipeline pipeline = ch.pipeline(); + + Http2FrameCodec http2Codec = Http2FrameCodecBuilder.forServer() + // simulate not sending goaway + .decoupleCloseAndGoAway(true) + .autoAckPingFrame(false) + .initialSettings(Http2Settings.defaultSettings().maxConcurrentStreams(2)) + .frameLogger(new Http2FrameLogger(LogLevel.DEBUG, "WIRE")) + .build(); + + Http2MultiplexHandler http2Handler = new Http2MultiplexHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new StreamHandler()); + } + }); + + pipeline.addLast(http2Codec); + pipeline.addLast(http2Handler); + } + + public void shutdown() throws InterruptedException { + group.shutdownGracefully().await(); + serverSock.close(); + } + + public int port() { + return serverSock.localAddress().getPort(); + } + } + + private static final class StreamHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2Frame http2Frame) throws Exception { + if (http2Frame instanceof Http2DataFrame) { + Http2DataFrame dataFrame = (Http2DataFrame) http2Frame; + if (dataFrame.isEndStream()) { + Http2Headers headers = new DefaultHttp2Headers().status("200"); + ctx.writeAndFlush(new DefaultHttp2HeadersFrame(headers, false)); + ctx.executor().scheduleAtFixedRate(() -> { + DefaultHttp2DataFrame respData = new DefaultHttp2DataFrame(Unpooled.wrappedBuffer("hello".getBytes()), false); + ctx.writeAndFlush(respData); + }, 0, 2, TimeUnit.SECONDS); + } + } + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerCloseConnectionTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerCloseConnectionTest.java new file mode 100644 index 000000000000..88e0c50ff43a --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerCloseConnectionTest.java @@ -0,0 +1,243 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.fault; + +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Frame; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2FrameLogger; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.reactivex.Flowable; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.reactivestreams.Publisher; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; +import software.amazon.awssdk.http.nio.netty.EmptyPublisher; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; +import software.amazon.awssdk.utils.AttributeMap; +import software.amazon.awssdk.utils.Logger; + + +/** + * Testing the scenario where the connection gets inactive without GOAWAY frame + */ +public class ServerCloseConnectionTest { + private static final Logger LOGGER = Logger.loggerFor(ServerCloseConnectionTest.class); + private SdkAsyncHttpClient netty; + private Server server; + + @Before + public void setup() throws Exception { + server = new Server(); + server.init(); + + netty = NettyNioAsyncHttpClient.builder() + .readTimeout(Duration.ofMillis(500)) + .eventLoopGroup(SdkEventLoopGroup.builder().numberOfThreads(3).build()) + .protocol(Protocol.HTTP2) + .buildWithDefaults(AttributeMap.builder().put(TRUST_ALL_CERTIFICATES, true).build()); + } + + @After + public void teardown() throws InterruptedException { + if (server != null) { + server.shutdown(); + } + server = null; + + if (netty != null) { + netty.close(); + } + netty = null; + } + + @Test + public void connectionGetsInactive_shouldNotReuse() { + server.ackPingOnFirstChannel = true; + // The first request picks up a bad channel and should fail. Channel 1 + CompletableFuture firstRequest = sendGetRequest(); + assertThatThrownBy(() -> firstRequest.join()) + .hasMessageContaining("An error occurred on the connection") + .hasCauseInstanceOf(IOException.class) + .hasRootCauseInstanceOf(ClosedChannelException.class); + + server.failOnFirstChannel = false; + + // The second request should establish a new connection instead of reusing the bad channel 2 + LOGGER.info(() -> "sending out the second request"); + sendGetRequest().join(); + + // should be 2 connections + assertThat(server.h2ConnectionCount.get()).isEqualTo(2); + } + + private CompletableFuture sendGetRequest() { + AsyncExecuteRequest req = AsyncExecuteRequest.builder() + .responseHandler(new SdkAsyncHttpResponseHandler() { + private SdkHttpResponse headers; + + @Override + public void onHeaders(SdkHttpResponse headers) { + this.headers = headers; + } + + @Override + public void onStream(Publisher stream) { + Flowable.fromPublisher(stream).forEach(b -> { + }); + } + + @Override + public void onError(Throwable error) { + } + }) + .request(SdkHttpFullRequest.builder() + .method(SdkHttpMethod.GET) + .protocol("https") + .host("localhost") + .port(server.port()) + .build()) + .requestContentPublisher(new EmptyPublisher()) + .build(); + + return netty.execute(req); + } + + + private static class Server extends ChannelInitializer { + private ServerBootstrap bootstrap; + private ServerSocketChannel serverSock; + private String[] channelIds = new String[5]; + private final NioEventLoopGroup group = new NioEventLoopGroup(); + private SslContext sslCtx; + private AtomicInteger h2ConnectionCount = new AtomicInteger(0); + private boolean ackPingOnFirstChannel = false; + private boolean failOnFirstChannel = true; + + void init() throws Exception { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + + bootstrap = new ServerBootstrap() + .channel(NioServerSocketChannel.class) + .group(group) + .childHandler(this); + + serverSock = (ServerSocketChannel) bootstrap.bind(0).sync().channel(); + } + + @Override + protected void initChannel(Channel ch) { + channelIds[h2ConnectionCount.get()] = ch.id().asShortText(); + ch.pipeline().addFirst(new LoggingHandler(LogLevel.DEBUG)); + LOGGER.debug(() -> "init channel " + ch); + h2ConnectionCount.incrementAndGet(); + + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(sslCtx.newHandler(ch.alloc())); + + + Http2FrameCodec http2Codec = Http2FrameCodecBuilder.forServer() + // simulate not sending goaway + .decoupleCloseAndGoAway(true) + .initialSettings(Http2Settings.defaultSettings().maxConcurrentStreams(2)) + .frameLogger(new Http2FrameLogger(LogLevel.DEBUG, "WIRE")) + .build(); + + Http2MultiplexHandler http2Handler = new Http2MultiplexHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) { + ch.pipeline().addLast(new MightCloseConnectionStreamFrameHandler()); + } + }); + + pipeline.addLast(http2Codec); + pipeline.addLast(http2Handler); + } + + public void shutdown() throws InterruptedException { + group.shutdownGracefully().await(); + serverSock.close(); + } + + public int port() { + return serverSock.localAddress().getPort(); + } + + private class MightCloseConnectionStreamFrameHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) { + if (frame instanceof Http2DataFrame) { + // Not respond if this is channel 1 + if (channelIds[0].equals(ctx.channel().parent().id().asShortText()) && failOnFirstChannel) { + ctx.channel().parent().close(); + } else { + DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(false); + try { + LOGGER.info(() -> "return empty data " + ctx.channel() + " frame " + frame.getClass()); + Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); + ctx.write(dataFrame); + ctx.write(new DefaultHttp2HeadersFrame(headers, true)); + ctx.flush(); + } finally { + dataFrame.release(); + } + } + } + } + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerNotRespondingTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerNotRespondingTest.java new file mode 100644 index 000000000000..92d3624febf1 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerNotRespondingTest.java @@ -0,0 +1,284 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.fault; + +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.DefaultHttp2DataFrame; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.DefaultHttp2PingFrame; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Frame; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2FrameLogger; +import io.netty.handler.codec.http2.Http2GoAwayFrame; +import io.netty.handler.codec.http2.Http2Headers; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.handler.codec.http2.Http2PingFrame; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import io.netty.handler.timeout.ReadTimeoutException; +import io.reactivex.Flowable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.reactivestreams.Publisher; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; +import software.amazon.awssdk.http.nio.netty.EmptyPublisher; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; +import software.amazon.awssdk.utils.AttributeMap; +import software.amazon.awssdk.utils.Logger; + + +/** + * Testing the scenario where the server fails to respond to periodic PING + */ +public class ServerNotRespondingTest { + private static final Logger LOGGER = Logger.loggerFor(ServerNotRespondingTest.class); + private SdkAsyncHttpClient netty; + private Server server; + + @Before + public void setup() throws Exception { + server = new Server(); + server.init(); + + netty = NettyNioAsyncHttpClient.builder() + .readTimeout(Duration.ofMillis(1000)) + .eventLoopGroup(SdkEventLoopGroup.builder().numberOfThreads(3).build()) + .http2Configuration(h -> h.healthCheckPingPeriod(Duration.ofMillis(200))) + .protocol(Protocol.HTTP2) + .buildWithDefaults(AttributeMap.builder().put(TRUST_ALL_CERTIFICATES, true).build()); + } + + @After + public void teardown() throws InterruptedException { + if (server != null) { + server.shutdown(); + } + server = null; + + if (netty != null) { + netty.close(); + } + netty = null; + } + + @Test + public void connectionNotAckPing_newRequestShouldUseNewConnection() throws InterruptedException { + server.ackPingOnFirstChannel = false; + server.notRespondOnFirstChannel = false; + CompletableFuture firstRequest = sendGetRequest(); + // First request should succeed + firstRequest.join(); + + // Wait for Ping to close the connection + Thread.sleep(200); + server.notRespondOnFirstChannel = false; + sendGetRequest().join(); + assertThat(server.h2ConnectionCount.get()).isEqualTo(2); + assertThat(server.closedByClientH2ConnectionCount.get()).isEqualTo(1); + } + + @Test + public void connectionNotRespond_newRequestShouldUseNewConnection() throws Exception { + server.ackPingOnFirstChannel = true; + server.notRespondOnFirstChannel = true; + + // The first request picks up a non-responding channel and should fail. Channel 1 + CompletableFuture firstRequest = sendGetRequest(); + + assertThatThrownBy(() -> firstRequest.join()).hasRootCauseInstanceOf(ReadTimeoutException.class); + + // The second request should pick up a new healthy channel - Channel 2 + sendGetRequest().join(); + + assertThat(server.h2ConnectionCount.get()).isEqualTo(2); + assertThat(server.closedByClientH2ConnectionCount.get()).isEqualTo(1); + } + + private CompletableFuture sendGetRequest() { + AsyncExecuteRequest req = AsyncExecuteRequest.builder() + .responseHandler(new SdkAsyncHttpResponseHandler() { + private SdkHttpResponse headers; + + @Override + public void onHeaders(SdkHttpResponse headers) { + this.headers = headers; + } + + @Override + public void onStream(Publisher stream) { + Flowable.fromPublisher(stream).forEach(b -> { + }); + } + + @Override + public void onError(Throwable error) { + } + }) + .request(SdkHttpFullRequest.builder() + .method(SdkHttpMethod.GET) + .protocol("https") + .host("localhost") + .port(server.port()) + .build()) + .requestContentPublisher(new EmptyPublisher()) + .build(); + + return netty.execute(req); + } + + + private static class Server extends ChannelInitializer { + private ServerBootstrap bootstrap; + private ServerSocketChannel serverSock; + private String[] channelIds = new String[5]; + private final NioEventLoopGroup group = new NioEventLoopGroup(); + private SslContext sslCtx; + private AtomicInteger h2ConnectionCount = new AtomicInteger(0); + private AtomicInteger closedByClientH2ConnectionCount = new AtomicInteger(0); + private volatile boolean ackPingOnFirstChannel = false; + private volatile boolean notRespondOnFirstChannel = true; + + void init() throws Exception { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + + bootstrap = new ServerBootstrap() + .channel(NioServerSocketChannel.class) + .group(group) + .childHandler(this); + + serverSock = (ServerSocketChannel) bootstrap.bind(0).sync().channel(); + } + + @Override + protected void initChannel(Channel ch) { + channelIds[h2ConnectionCount.get()] = ch.id().asShortText(); + ch.pipeline().addFirst(new LoggingHandler(LogLevel.DEBUG)); + LOGGER.debug(() -> "init channel " + ch); + h2ConnectionCount.incrementAndGet(); + + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast(sslCtx.newHandler(ch.alloc())); + + + Http2FrameCodec http2Codec = Http2FrameCodecBuilder.forServer() + //Disable auto ack ping + .autoAckPingFrame(false) + .initialSettings(Http2Settings.defaultSettings().maxConcurrentStreams(2)) + .frameLogger(new Http2FrameLogger(LogLevel.DEBUG, "WIRE")) + .build(); + + Http2MultiplexHandler http2Handler = new Http2MultiplexHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel ch) throws Exception { + ch.pipeline().addLast(new MightNotRespondStreamFrameHandler()); + } + }); + + pipeline.addLast(http2Codec); + pipeline.addLast(new MightNotRespondPingFrameHandler()); + pipeline.addLast(new VerifyGoAwayFrameHandler()); + pipeline.addLast(http2Handler); + } + + public void shutdown() throws InterruptedException { + group.shutdownGracefully().await(); + serverSock.close(); + } + + public int port() { + return serverSock.localAddress().getPort(); + } + + public final class MightNotRespondPingFrameHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2PingFrame msg) { + if (channelIds[0].equals(ctx.channel().id().asShortText()) && !ackPingOnFirstChannel) { + // Not respond if this is the first request + LOGGER.info(() -> "yolo" + ctx.channel()); + } else { + ctx.writeAndFlush(new DefaultHttp2PingFrame(msg.content(), true)); + } + } + } + + + public final class VerifyGoAwayFrameHandler extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2GoAwayFrame msg) { + LOGGER.info(() -> "goaway" + ctx.channel()); + closedByClientH2ConnectionCount.incrementAndGet(); + msg.release(); + } + } + + private class MightNotRespondStreamFrameHandler extends SimpleChannelInboundHandler { + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2Frame frame) { + if (frame instanceof Http2DataFrame) { + // Not respond if this is channel 1 + if (channelIds[0].equals(ctx.channel().parent().id().asShortText()) && notRespondOnFirstChannel) { + LOGGER.info(() -> "This is the first request, not responding" + ctx.channel()); + } else { + DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(false); + try { + LOGGER.info(() -> "return empty data " + ctx.channel() + " frame " + frame.getClass()); + Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText()); + ctx.write(dataFrame); + ctx.write(new DefaultHttp2HeadersFrame(headers, true)); + ctx.flush(); + } finally { + dataFrame.release(); + } + } + } + } + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java index 18add028a262..1289e8af0c21 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -23,11 +23,6 @@ import static software.amazon.awssdk.http.SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS; import static software.amazon.awssdk.http.SdkHttpConfigurationOption.TLS_KEY_MANAGERS_PROVIDER; -import com.github.tomakehurst.wiremock.junit.WireMockRule; -import io.netty.channel.Channel; -import io.netty.channel.pool.ChannelPool; -import io.netty.handler.ssl.SslProvider; -import io.netty.util.concurrent.Future; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; @@ -35,10 +30,19 @@ import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Rule; import org.junit.Test; +import org.mockito.Mockito; + +import io.netty.channel.Channel; +import io.netty.channel.pool.ChannelPool; +import io.netty.handler.ssl.SslProvider; +import io.netty.util.concurrent.Future; import software.amazon.awssdk.http.Protocol; import software.amazon.awssdk.http.TlsKeyManagersProvider; import software.amazon.awssdk.http.nio.netty.ProxyConfiguration; @@ -94,6 +98,63 @@ public void close_underlyingPoolsShouldBeClosed() { }); } + @Test + public void get_callsInjectedBootstrapProviderCorrectly() { + BootstrapProvider bootstrapProvider = Mockito.spy( + new BootstrapProvider(SdkEventLoopGroup.builder().build(), + new NettyConfiguration(GLOBAL_HTTP_DEFAULTS), + new SdkChannelOptions())); + + URI targetUri = URI.create("https://some-awesome-service-1234.amazonaws.com:8080"); + + AwaitCloseChannelPoolMap.Builder builder = + AwaitCloseChannelPoolMap.builder() + .sdkChannelOptions(new SdkChannelOptions()) + .sdkEventLoopGroup(SdkEventLoopGroup.builder().build()) + .configuration(new NettyConfiguration(GLOBAL_HTTP_DEFAULTS)) + .protocol(Protocol.HTTP1_1) + .maxStreams(100) + .sslProvider(SslProvider.OPENSSL); + + channelPoolMap = new AwaitCloseChannelPoolMap(builder, null, bootstrapProvider); + channelPoolMap.get(targetUri); + + verify(bootstrapProvider).createBootstrap("some-awesome-service-1234.amazonaws.com", 8080); + } + + @Test + public void get_usingProxy_callsInjectedBootstrapProviderCorrectly() { + BootstrapProvider bootstrapProvider = Mockito.spy( + new BootstrapProvider(SdkEventLoopGroup.builder().build(), + new NettyConfiguration(GLOBAL_HTTP_DEFAULTS), + new SdkChannelOptions())); + + URI targetUri = URI.create("https://some-awesome-service-1234.amazonaws.com:8080"); + Map shouldProxyCache = new HashMap<>(); + shouldProxyCache.put(targetUri, true); + + ProxyConfiguration proxyConfiguration = + ProxyConfiguration.builder() + .host("localhost") + .port(mockProxy.port()) + .build(); + + AwaitCloseChannelPoolMap.Builder builder = + AwaitCloseChannelPoolMap.builder() + .proxyConfiguration(proxyConfiguration) + .sdkChannelOptions(new SdkChannelOptions()) + .sdkEventLoopGroup(SdkEventLoopGroup.builder().build()) + .configuration(new NettyConfiguration(GLOBAL_HTTP_DEFAULTS)) + .protocol(Protocol.HTTP1_1) + .maxStreams(100) + .sslProvider(SslProvider.OPENSSL); + + channelPoolMap = new AwaitCloseChannelPoolMap(builder, shouldProxyCache, bootstrapProvider); + channelPoolMap.get(targetUri); + + verify(bootstrapProvider).createBootstrap("localhost", mockProxy.port()); + } + @Test public void usingProxy_usesCachedValueWhenPresent() { URI targetUri = URI.create("https://some-awesome-service-1234.amazonaws.com"); @@ -117,7 +178,7 @@ public void usingProxy_usesCachedValueWhenPresent() { .maxStreams(100) .sslProvider(SslProvider.OPENSSL); - channelPoolMap = new AwaitCloseChannelPoolMap(builder, shouldProxyCache); + channelPoolMap = new AwaitCloseChannelPoolMap(builder, shouldProxyCache, null); // The target host does not exist so acquiring a channel should fail unless we're configured to connect to // the mock proxy host for this URI. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java new file mode 100644 index 000000000000..dc3789a34aab --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.http.SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import io.netty.bootstrap.Bootstrap; +import io.netty.resolver.AddressResolver; +import io.netty.resolver.AddressResolverGroup; +import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; + +@RunWith(MockitoJUnitRunner.class) +public class BootstrapProviderTest { + private final BootstrapProvider bootstrapProvider = + new BootstrapProvider(SdkEventLoopGroup.builder().build(), + new NettyConfiguration(GLOBAL_HTTP_DEFAULTS), + new SdkChannelOptions()); + + // IMPORTANT: This unit test asserts that the bootstrap provider creates bootstraps using 'unresolved + // InetSocketAddress'. If this test is replaced or removed, perhaps due to a different implementation of + // SocketAddress, a different test must be created that ensures that the hostname will be resolved on every + // connection attempt and not cached between connection attempts. + @Test + public void createBootstrap_usesUnresolvedInetSocketAddress() { + Bootstrap bootstrap = bootstrapProvider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443); + + SocketAddress socketAddress = bootstrap.config().remoteAddress(); + + assertThat(socketAddress).isInstanceOf(InetSocketAddress.class); + InetSocketAddress inetSocketAddress = (InetSocketAddress)socketAddress; + + assertThat(inetSocketAddress.isUnresolved()).isTrue(); + } +} \ No newline at end of file diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPoolTest.java index 616a7b416ae1..235223a5d5be 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/CancellableAcquireChannelPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializerTest.java new file mode 100644 index 000000000000..12e0627bb1e4 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ChannelPipelineInitializerTest.java @@ -0,0 +1,75 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static software.amazon.awssdk.http.SdkHttpConfigurationOption.GLOBAL_HTTP_DEFAULTS; + +import io.netty.buffer.UnpooledByteBufAllocator; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.channel.pool.ChannelPool; +import io.netty.handler.codec.http2.Http2SecurityUtil; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; +import io.netty.handler.ssl.SupportedCipherSuiteFilter; +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicReference; +import javax.net.ssl.SSLException; +import org.junit.Test; +import software.amazon.awssdk.http.Protocol; + +public class ChannelPipelineInitializerTest { + + private ChannelPipelineInitializer pipelineInitializer; + + private URI targetUri; + + @Test + public void channelConfigOptionCheck() throws SSLException { + targetUri = URI.create("https://some-awesome-service-1234.amazonaws.com:8080"); + + SslContext sslContext = SslContextBuilder.forClient() + .sslProvider(SslProvider.JDK) + .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) + .build(); + + AtomicReference channelPoolRef = new AtomicReference<>(); + + NettyConfiguration nettyConfiguration = new NettyConfiguration(GLOBAL_HTTP_DEFAULTS); + + pipelineInitializer = new ChannelPipelineInitializer(Protocol.HTTP1_1, + sslContext, + SslProvider.JDK, + 100, + 1024, + Duration.ZERO, + channelPoolRef, + nettyConfiguration, + targetUri); + + Channel channel = new EmbeddedChannel(); + + pipelineInitializer.channelCreated(channel); + + assertThat(channel.config().getOption(ChannelOption.ALLOCATOR), is(UnpooledByteBufAllocator.DEFAULT)); + + } +} \ No newline at end of file diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ConnectionReaperTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ConnectionReaperTest.java index d596f60a9433..33fb9d9b906b 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ConnectionReaperTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ConnectionReaperTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FullResponseContentPublisherTckTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FullResponseContentPublisherTckTest.java index f8650a4dcab9..962194450366 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FullResponseContentPublisherTckTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FullResponseContentPublisherTckTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandlerTest.java index 083a3036dbf2..d28cf1558cd5 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/FutureCancelHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/GoAwayTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/GoAwayTest.java deleted file mode 100644 index cec6ff8a3c1a..000000000000 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/GoAwayTest.java +++ /dev/null @@ -1,331 +0,0 @@ -package software.amazon.awssdk.http.nio.netty.internal; - -import static org.assertj.core.api.Assertions.assertThat; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.ServerSocketChannel; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http2.DefaultHttp2FrameReader; -import io.netty.handler.codec.http2.DefaultHttp2FrameWriter; -import io.netty.handler.codec.http2.DefaultHttp2Headers; -import io.netty.handler.codec.http2.Http2FrameAdapter; -import io.netty.handler.codec.http2.Http2FrameListener; -import io.netty.handler.codec.http2.Http2FrameReader; -import io.netty.handler.codec.http2.Http2FrameWriter; -import io.netty.handler.codec.http2.Http2Headers; -import io.netty.handler.codec.http2.Http2Settings; -import io.netty.util.AttributeKey; -import io.reactivex.Flowable; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.function.Supplier; -import org.junit.After; -import org.junit.Test; -import org.reactivestreams.Publisher; -import software.amazon.awssdk.http.Protocol; -import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.http.SdkHttpMethod; -import software.amazon.awssdk.http.SdkHttpResponse; -import software.amazon.awssdk.http.async.AsyncExecuteRequest; -import software.amazon.awssdk.http.async.SdkAsyncHttpClient; -import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; -import software.amazon.awssdk.http.nio.netty.EmptyPublisher; -import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; -import software.amazon.awssdk.http.nio.netty.SdkEventLoopGroup; - -/** - * Tests to ensure that the client behaves as expected when it receives GOAWAY messages. - */ -public class GoAwayTest { - - private SdkAsyncHttpClient netty; - private SimpleEndpointDriver endpointDriver; - - @After - public void teardown() throws InterruptedException { - if (endpointDriver != null) { - endpointDriver.shutdown(); - } - endpointDriver = null; - - if (netty != null) { - netty.close(); - } - netty = null; - } - - @Test - public void execute_goAwayReceived_existingChannelsNotReused() throws InterruptedException { - // Frame listener supplier for each connection - Supplier frameListenerSupplier = () -> new TestFrameListener() { - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { - frameWriter().writeHeaders(ctx, streamId, new DefaultHttp2Headers().add("content-length", "0").status("204"), 0, true, ctx.newPromise()); - ctx.flush(); - } - }; - - endpointDriver = new SimpleEndpointDriver(frameListenerSupplier); - endpointDriver.init(); - - netty = NettyNioAsyncHttpClient.builder() - .eventLoopGroup(SdkEventLoopGroup.builder().numberOfThreads(1).build()) - .protocol(Protocol.HTTP2) - .build(); - - sendGetRequest().join(); - - // Note: It's possible the initial request can cause the client to allocate more than 1 channel - int initialChannelNum = endpointDriver.channels.size(); - - // Send GOAWAY to all the currently open channels - endpointDriver.channels.forEach(ch -> endpointDriver.goAway(ch, 1)); - - // Need to give a chance for the streams to get closed - Thread.sleep(1000); - - // Since the existing channels are now invalid, this request should cause a new channel to be opened - sendGetRequest().join(); - - assertThat(endpointDriver.channels).hasSize(initialChannelNum + 1); - } - - // The client should not close streams that are less than the 'last stream - // ID' given in the GOAWAY frame since it means they were processed fully - @Test - public void execute_goAwayReceived_lastStreamId_lowerStreamsNotClosed() throws InterruptedException { - ConcurrentHashMap> channelToStreams = new ConcurrentHashMap<>(); - - CompletableFuture stream3Received = new CompletableFuture<>(); - CountDownLatch allRequestsReceived = new CountDownLatch(2); - byte[] getPayload = "go away!".getBytes(StandardCharsets.UTF_8); - Supplier frameListenerSupplier = () -> new TestFrameListener() { - @Override - public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) { - channelToStreams.computeIfAbsent(ctx.channel().id().asShortText(), (k) -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(streamId); - - if (streamId == 3) { - stream3Received.complete(null); - } - - if (streamId < 5) { - Http2Headers outboundHeaders = new DefaultHttp2Headers() - .status("200") - .add("content-type", "text/plain") - .addInt("content-length", getPayload.length); - - frameWriter().writeHeaders(ctx, streamId, outboundHeaders, 0, false, ctx.newPromise()); - ctx.flush(); - } - - allRequestsReceived.countDown(); - } - }; - - endpointDriver = new SimpleEndpointDriver(frameListenerSupplier); - endpointDriver.init(); - - netty = NettyNioAsyncHttpClient.builder() - .protocol(Protocol.HTTP2) - .build(); - - CompletableFuture stream3Cf = sendGetRequest();// stream ID 3 - - // Wait for the request to be received just to ensure that it is given ID 3 - stream3Received.join(); - - CompletableFuture stream5Cf = sendGetRequest();// stream ID 5 - - allRequestsReceived.await(); - - // send the GOAWAY first, specifying that everything after 3 is not processed - endpointDriver.channels.forEach(ch -> { - Set streams = channelToStreams.getOrDefault(ch.id().asShortText(), Collections.emptySet()); - if (streams.contains(3)) { - endpointDriver.goAway(ch, 3); - } - }); - - // now send the DATA for stream 3, which should still be valid - endpointDriver.channels.forEach(ch -> { - Set streams = channelToStreams.getOrDefault(ch.id().asShortText(), Collections.emptySet()); - if (streams.contains(3)) { - endpointDriver.data(ch, 3, getPayload); - } - }); - - waitForFuture(stream3Cf); - waitForFuture(stream5Cf); - - assertThat(stream3Cf.isCompletedExceptionally()).isFalse(); - assertThat(stream5Cf.isCompletedExceptionally()).isTrue(); - } - - private CompletableFuture sendGetRequest() { - AsyncExecuteRequest req = AsyncExecuteRequest.builder() - .responseHandler(new SdkAsyncHttpResponseHandler() { - private SdkHttpResponse headers; - - @Override - public void onHeaders(SdkHttpResponse headers) { - this.headers = headers; - } - - @Override - public void onStream(Publisher stream) { - // Consume the stream in order to complete request - Flowable.fromPublisher(stream).forEach(b -> {}); - } - - @Override - public void onError(Throwable error) { - } - }) - .request(SdkHttpFullRequest.builder() - .method(SdkHttpMethod.GET) - .protocol("http") - .host("localhost") - .port(endpointDriver.port()) - .build()) - .requestContentPublisher(new EmptyPublisher()) - .build(); - - return netty.execute(req); - } - - private static void waitForFuture(CompletableFuture cf) { - try { - cf.join(); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - // Minimal class to simulate an H2 endpoint - private static class SimpleEndpointDriver extends ChannelInitializer { - private List channels = new ArrayList<>(); - private final NioEventLoopGroup group = new NioEventLoopGroup(); - private final Supplier frameListenerSupplier; - private ServerBootstrap bootstrap; - private ServerSocketChannel serverSock; - - public SimpleEndpointDriver(Supplier frameListenerSupplier) { - this.frameListenerSupplier = frameListenerSupplier; - } - - public void init() throws InterruptedException { - bootstrap = new ServerBootstrap() - .channel(NioServerSocketChannel.class) - .group(new NioEventLoopGroup()) - .childHandler(this) - .childOption(ChannelOption.SO_KEEPALIVE, true); - - serverSock = (ServerSocketChannel) bootstrap.bind(0).sync().channel(); - } - - public void shutdown() throws InterruptedException { - group.shutdownGracefully().await(); - } - - public int port() { - return serverSock.localAddress().getPort(); - } - - public void goAway(SocketChannel ch, int lastStreamId) { - ByteBuf b = ch.alloc().buffer(9 + 8); - - // Frame header - b.writeMedium(8); // Payload length - b.writeByte(0x7); // Type = GOAWAY - b.writeByte(0x0); // Flags - b.writeInt(0); // 0 = connection frame - - // GOAWAY payload - b.writeInt(lastStreamId); - b.writeInt(0); // Error code - - ch.writeAndFlush(b); - } - - public void data(SocketChannel ch, int streamId, byte[] payload) { - ByteBuf b = ch.alloc().buffer(9 + payload.length); - - // Header - b.writeMedium(payload.length); // Payload length - b.writeByte(0); // Type = DATA - b.writeByte(0x1); // 0x1 = EOF - b.writeInt(streamId); - - // Payload - b.writeBytes(payload); - - ch.writeAndFlush(b); - } - - @Override - protected void initChannel(SocketChannel ch) throws Exception { - channels.add(ch); - ch.pipeline().addLast(new Http2ConnHandler(this, frameListenerSupplier.get())); - } - } - - private abstract class TestFrameListener extends Http2FrameAdapter { - private final Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter(); - - protected final Http2FrameWriter frameWriter() { - return frameWriter; - } - - @Override - public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) { - frameWriter().writeSettings(ctx, new Http2Settings(), ctx.newPromise()); - frameWriter().writeSettingsAck(ctx, ctx.newPromise()); - ctx.flush(); - } - } - - private static class Http2ConnHandler extends ChannelDuplexHandler { - // Prior knowledge preface - private static final String PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; - private static final AttributeKey H2_ESTABLISHED = AttributeKey.newInstance("h2-etablished"); - - private final Http2FrameReader frameReader = new DefaultHttp2FrameReader(); - private final SimpleEndpointDriver simpleEndpointDriver; - private final Http2FrameListener frameListener; - - public Http2ConnHandler(SimpleEndpointDriver simpleEndpointDriver, Http2FrameListener frameListener) { - this.simpleEndpointDriver = simpleEndpointDriver; - this.frameListener = frameListener; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - ByteBuf bb = (ByteBuf) msg; - if (!isH2Established(ctx.channel())) { - String prefaceString = bb.readCharSequence(24, StandardCharsets.UTF_8).toString(); - if (PREFACE.equals(prefaceString)) { - ctx.channel().attr(H2_ESTABLISHED).set(true); - } - } - frameReader.readFrame(ctx, bb, frameListener); - } - - private boolean isH2Established(Channel ch) { - return Boolean.TRUE.equals(ch.attr(H2_ESTABLISHED).get()); - } - } -} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPoolTest.java index f7a1922923ef..5e6213972598 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HandlerRemovingChannelPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.REQUEST_CONTEXT_KEY; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.RESPONSE_COMPLETE_KEY; -import com.typesafe.netty.http.HttpStreamsClientHandler; +import software.amazon.awssdk.http.nio.netty.internal.nrs.HttpStreamsClientHandler; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; import io.netty.channel.nio.NioEventLoopGroup; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPoolTest.java index b3af40680ca6..66a84fafa57e 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HealthCheckedChannelPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPoolTest.java index 9a53858d782a..71a0203794b2 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/HonorCloseOnReleaseChannelPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPoolTest.java index 50cc17621c10..3789749730bc 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/Http1TunnelConnectionPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandlerTest.java index c51929051f1f..33263a5911d8 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallowerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallowerTest.java index c03787e8ebea..75e90ef19f11 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallowerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/LastHttpContentSwallowerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/MockChannel.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/MockChannel.java index 49dc9caa634b..37de10f36294 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/MockChannel.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/MockChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutorTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutorTest.java index 468deb036926..be7093195e55 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutorTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/NettyRequestExecutorTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; import static org.assertj.core.api.Assertions.assertThat; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandlerTest.java index 90332c9e74fa..e5e3f6f57210 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OldConnectionReaperHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandlerTest.java index 469a60d4c9ac..b73355edb68d 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/OneTimeReadTimeoutHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandlerTest.java index 887d1b2fa3f9..d30f9f0252ef 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ProxyTunnelInitHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/PublisherAdapterTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/PublisherAdapterTest.java index 58d9f8f41062..068e870e3209 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/PublisherAdapterTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/PublisherAdapterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -25,8 +25,8 @@ import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.REQUEST_CONTEXT_KEY; -import com.typesafe.netty.http.DefaultStreamedHttpResponse; -import com.typesafe.netty.http.StreamedHttpResponse; +import software.amazon.awssdk.http.nio.netty.internal.nrs.DefaultStreamedHttpResponse; +import software.amazon.awssdk.http.nio.netty.internal.nrs.StreamedHttpResponse; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.EmptyByteBuf; import io.netty.channel.ChannelHandlerContext; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapterTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapterTest.java new file mode 100644 index 000000000000..31e2aca3c9e7 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/RequestAdapterTest.java @@ -0,0 +1,196 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http2.HttpConversionUtil; +import java.net.URI; +import java.util.List; +import org.junit.Test; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; + +public class RequestAdapterTest { + + private final RequestAdapter h1Adapter = new RequestAdapter(Protocol.HTTP1_1); + private final RequestAdapter h2Adapter = new RequestAdapter(Protocol.HTTP2); + + @Test + public void adapt_h1Request_requestIsCorrect() { + SdkHttpRequest request = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:12345/foo/bar/baz")) + .putRawQueryParameter("foo", "bar") + .putRawQueryParameter("bar", "baz") + .putHeader("header1", "header1val") + .putHeader("header2", "header2val") + .method(SdkHttpMethod.GET) + .build(); + + HttpRequest adapted = h1Adapter.adapt(request); + + assertThat(adapted.method()).isEqualTo(HttpMethod.valueOf("GET")); + assertThat(adapted.uri()).isEqualTo("/foo/bar/baz?foo=bar&bar=baz"); + assertThat(adapted.protocolVersion()).isEqualTo(HttpVersion.HTTP_1_1); + assertThat(adapted.headers().getAll("Host")).containsExactly("localhost:12345"); + assertThat(adapted.headers().getAll("header1")).containsExactly("header1val"); + assertThat(adapted.headers().getAll("header2")).containsExactly("header2val"); + } + + @Test + public void adapt_h2Request_addsSchemeExtension() { + SdkHttpRequest request = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:12345/foo/bar/baz")) + .putRawQueryParameter("foo", "bar") + .putRawQueryParameter("bar", "baz") + .putHeader("header1", "header1val") + .putHeader("header2", "header2val") + .method(SdkHttpMethod.GET) + .build(); + + HttpRequest adapted = h2Adapter.adapt(request); + + assertThat(adapted.headers().getAll(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text())).containsExactly("http"); + } + + @Test + public void adapt_noPathContainsQueryParams() { + SdkHttpRequest request = SdkHttpRequest.builder() + .host("localhost:12345") + .protocol("http") + .putRawQueryParameter("foo", "bar") + .putRawQueryParameter("bar", "baz") + .putHeader("header1", "header1val") + .putHeader("header2", "header2val") + .method(SdkHttpMethod.GET) + .build(); + + HttpRequest adapted = h1Adapter.adapt(request); + + assertThat(adapted.method()).isEqualTo(HttpMethod.valueOf("GET")); + assertThat(adapted.uri()).isEqualTo("/?foo=bar&bar=baz"); + assertThat(adapted.protocolVersion()).isEqualTo(HttpVersion.HTTP_1_1); + assertThat(adapted.headers().getAll("Host")).containsExactly("localhost:12345"); + } + + @Test + public void adapt_hostHeaderSet() { + SdkHttpRequest sdkRequest = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:12345/")) + .method(SdkHttpMethod.HEAD) + .build(); + HttpRequest result = h1Adapter.adapt(sdkRequest); + List hostHeaders = result.headers() + .getAll(HttpHeaderNames.HOST.toString()); + assertThat(hostHeaders).containsExactly("localhost:12345"); + } + + @Test + public void adapt_standardHttpsPort_omittedInHeader() { + SdkHttpRequest sdkRequest = SdkHttpRequest.builder() + .uri(URI.create("https://localhost:443/")) + .method(SdkHttpMethod.HEAD) + .build(); + HttpRequest result = h1Adapter.adapt(sdkRequest); + List hostHeaders = result.headers() + .getAll(HttpHeaderNames.HOST.toString()); + assertThat(hostHeaders).containsExactly("localhost"); + } + + @Test + public void adapt_containsQueryParamsRequiringEncoding() { + SdkHttpRequest request = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:12345")) + .putRawQueryParameter("java", "☕") + .putRawQueryParameter("python", "\uD83D\uDC0D") + .method(SdkHttpMethod.GET) + .build(); + + HttpRequest adapted = h1Adapter.adapt(request); + + assertThat(adapted.uri()).isEqualTo("/?java=%E2%98%95&python=%F0%9F%90%8D"); + } + + @Test + public void adapt_pathEmpty_setToRoot() { + SdkHttpRequest request = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:12345")) + .method(SdkHttpMethod.GET) + .build(); + + HttpRequest adapted = h1Adapter.adapt(request); + + assertThat(adapted.uri()).isEqualTo("/"); + } + + @Test + public void adapt_defaultPortUsed() { + SdkHttpRequest sdkRequest = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:80/")) + .method(SdkHttpMethod.HEAD) + .build(); + HttpRequest result = h1Adapter.adapt(sdkRequest); + List hostHeaders = result.headers() + .getAll(HttpHeaderNames.HOST.toString()); + assertNotNull(hostHeaders); + assertEquals(1, hostHeaders.size()); + assertEquals("localhost", hostHeaders.get(0)); + + sdkRequest = SdkHttpRequest.builder() + .uri(URI.create("https://localhost:443/")) + .method(SdkHttpMethod.HEAD) + .build(); + result = h1Adapter.adapt(sdkRequest); + hostHeaders = result.headers() + .getAll(HttpHeaderNames.HOST.toString()); + assertNotNull(hostHeaders); + assertEquals(1, hostHeaders.size()); + assertEquals("localhost", hostHeaders.get(0)); + } + + @Test + public void adapt_nonStandardHttpPort() { + SdkHttpRequest sdkRequest = SdkHttpRequest.builder() + .uri(URI.create("http://localhost:8080/")) + .method(SdkHttpMethod.HEAD) + .build(); + HttpRequest result = h1Adapter.adapt(sdkRequest); + List hostHeaders = result.headers() + .getAll(HttpHeaderNames.HOST.toString()); + + assertThat(hostHeaders).containsExactly("localhost:8080"); + } + + @Test + public void adapt_nonStandardHttpsPort() { + SdkHttpRequest sdkRequest = SdkHttpRequest.builder() + .uri(URI.create("https://localhost:8443/")) + .method(SdkHttpMethod.HEAD) + .build(); + HttpRequest result = h1Adapter.adapt(sdkRequest); + List hostHeaders = result.headers() + .getAll(HttpHeaderNames.HOST.toString()); + + assertThat(hostHeaders).containsExactly("localhost:8443"); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ResponseCompletionTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ResponseCompletionTest.java index a266e8b5ae88..0f6f786eb9d0 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ResponseCompletionTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/ResponseCompletionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptionsTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptionsTest.java index 28f2b8c58d00..49fbf116b674 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptionsTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SdkChannelOptionsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroupTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroupTest.java index 62d58084394d..755630f1ee31 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroupTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SharedSdkEventLoopGroupTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandlerTest.java index cb9d27620096..1e1dcea1c6a9 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/SslCloseCompletionEventHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpiTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpiTest.java index 1703669a6346..58ef2ec6307e 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpiTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactorySpiTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactoryTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactoryTest.java index 1f5c8713e769..4e79ad583d5e 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactoryTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/StaticKeyManagerFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandlerTest.java index 1582af54cd97..3b87946e6c22 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/UnusedChannelExceptionHandlerTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal; import static org.assertj.core.api.Assertions.assertThat; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/FlushOnReadTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/FlushOnReadTest.java new file mode 100644 index 000000000000..aa7b2f545cc8 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/FlushOnReadTest.java @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class FlushOnReadTest { + + @Mock + private ChannelHandlerContext mockCtx; + + @Mock + private Channel mockChannel; + + @Mock + private Channel mockParentChannel; + + @Test + public void read_forwardsReadBeforeParentFlush() { + when(mockCtx.channel()).thenReturn(mockChannel); + when(mockChannel.parent()).thenReturn(mockParentChannel); + + FlushOnReadHandler handler = FlushOnReadHandler.getInstance(); + + handler.read(mockCtx); + + InOrder inOrder = Mockito.inOrder(mockCtx, mockParentChannel); + + inOrder.verify(mockCtx).read(); + inOrder.verify(mockParentChannel).flush(); + } + + @Test + public void getInstance_returnsSingleton() { + assertThat(FlushOnReadHandler.getInstance() == FlushOnReadHandler.getInstance()).isTrue(); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayEventListenerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayEventListenerTest.java new file mode 100644 index 000000000000..d642649dbea2 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayEventListenerTest.java @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http2.DefaultHttp2GoAwayFrame; +import io.netty.util.Attribute; +import org.junit.Before; +import org.junit.Test; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; + +public class Http2GoAwayEventListenerTest { + private ChannelHandlerContext ctx; + private Channel channel; + private ChannelPipeline channelPipeline; + private Attribute attribute; + + @Before + public void setup() { + this.ctx = mock(ChannelHandlerContext.class); + this.channel = mock(Channel.class); + this.channelPipeline = mock(ChannelPipeline.class); + this.attribute = mock(Attribute.class); + + when(ctx.channel()).thenReturn(channel); + when(channel.pipeline()).thenReturn(channelPipeline); + when(channel.attr(ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL)).thenReturn(attribute); + } + + @Test + public void goAwayWithNoChannelPoolRecordRaisesNoExceptions() throws Exception { + when(attribute.get()).thenReturn(null); + new Http2GoAwayEventListener(channel).onGoAwayReceived(0, 0, Unpooled.EMPTY_BUFFER); + verify(channelPipeline).fireExceptionCaught(isA(GoAwayException.class)); + } + + @Test + public void goAwayWithChannelPoolRecordPassesAlongTheFrame() throws Exception { + Http2MultiplexedChannelPool record = mock(Http2MultiplexedChannelPool.class); + when(attribute.get()).thenReturn(record); + new Http2GoAwayEventListener(channel).onGoAwayReceived(0, 0, Unpooled.EMPTY_BUFFER); + verify(record).handleGoAway(eq(channel), eq(0), isA(GoAwayException.class)); + verifyNoMoreInteractions(record); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayFrameHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayFrameHandlerTest.java deleted file mode 100644 index 2d9ed3ae4975..000000000000 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2GoAwayFrameHandlerTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http2.DefaultHttp2GoAwayFrame; -import io.netty.util.Attribute; -import org.junit.Before; -import org.junit.Test; -import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; - -public class Http2GoAwayFrameHandlerTest { - private static final DefaultHttp2GoAwayFrame GO_AWAY_FRAME = new DefaultHttp2GoAwayFrame(0, Unpooled.EMPTY_BUFFER); - private ChannelHandlerContext ctx; - private Channel channel; - private Attribute attribute; - - @Before - public void setup() { - this.ctx = mock(ChannelHandlerContext.class); - this.channel = mock(Channel.class); - this.attribute = mock(Attribute.class); - - when(ctx.channel()).thenReturn(channel); - when(channel.attr(ChannelAttributeKey.CHANNEL_POOL_RECORD)).thenReturn(attribute); - } - - @Test - public void goAwayWithNoChannelPoolRecordRaisesNoExceptions() { - when(attribute.get()).thenReturn(null); - new Http2GoAwayFrameHandler().channelRead0(ctx, GO_AWAY_FRAME); - } - - @Test - public void goAwayWithChannelPoolRecordPassesAlongTheFrame() { - MultiplexedChannelRecord record = mock(MultiplexedChannelRecord.class); - when(attribute.get()).thenReturn(record); - new Http2GoAwayFrameHandler().channelRead0(ctx, GO_AWAY_FRAME); - verify(record).goAway(GO_AWAY_FRAME); - verifyNoMoreInteractions(record); - } -} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPoolTest.java index 753988ad5964..ca40d14cb6e0 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2MultiplexedChannelPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,24 +15,40 @@ package software.amazon.awssdk.http.nio.netty.internal.http2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_CONNECTION; +import static software.amazon.awssdk.http.nio.netty.internal.http2.utils.Http2TestUtils.newHttp2Channel; + import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.pool.ChannelPool; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.http2.Http2Connection; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2LocalFlowController; +import io.netty.handler.codec.http2.Http2Stream; import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.FailedFuture; +import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; +import java.io.IOException; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mockito; - -import java.util.Collections; -import java.util.concurrent.CompletableFuture; - -import static org.assertj.core.api.Assertions.assertThat; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; /** * Tests for {@link Http2MultiplexedChannelPool}. @@ -51,29 +67,40 @@ public static void teardown() { } @Test - public void closeWaitsForConnectionToBeReleasedBeforeClosingConnectionPool() throws InterruptedException { + public void failedConnectionAcquireNotifiesPromise() throws InterruptedException { + IOException exception = new IOException(); + ChannelPool connectionPool = mock(ChannelPool.class); + when(connectionPool.acquire()).thenReturn(new FailedFuture<>(loopGroup.next(), exception)); + + ChannelPool pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup.next(), null); + + Future acquirePromise = pool.acquire().await(); + assertThat(acquirePromise.isSuccess()).isFalse(); + assertThat(acquirePromise.cause()).isEqualTo(exception); + } + + @Test + public void releaseParentChannelIfReleasingLastChildChannelOnGoAwayChannel() { SocketChannel channel = new NioSocketChannel(); try { loopGroup.register(channel).awaitUninterruptibly(); - Promise channelPromise = new DefaultPromise<>(loopGroup.next()); - channelPromise.setSuccess(channel); - ChannelPool connectionPool = Mockito.mock(ChannelPool.class); - Promise releasePromise = Mockito.spy(new DefaultPromise<>(loopGroup.next())); - Mockito.doCallRealMethod().when(releasePromise).await(); - releasePromise.setSuccess(null); - Mockito.when(connectionPool.release(Mockito.eq(channel))).thenReturn(releasePromise); + ChannelPool connectionPool = mock(ChannelPool.class); + ArgumentCaptor releasePromise = ArgumentCaptor.forClass(Promise.class); + when(connectionPool.release(eq(channel), releasePromise.capture())).thenAnswer(invocation -> { + Promise promise = releasePromise.getValue(); + promise.setSuccess(null); + return promise; + }); - MultiplexedChannelRecord record = new MultiplexedChannelRecord(channelPromise, - channel, - 8, - (ch, rec) -> {}); - Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup.next(), 2, Collections.singletonList(record)); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 8, null); + Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup, + Collections.singleton(record), null); h2Pool.close(); - InOrder inOrder = Mockito.inOrder(connectionPool, releasePromise); - inOrder.verify(releasePromise).await(); + InOrder inOrder = Mockito.inOrder(connectionPool); + inOrder.verify(connectionPool).release(eq(channel), isA(Promise.class)); inOrder.verify(connectionPool).close(); } finally { channel.close().awaitUninterruptibly(); @@ -82,13 +109,67 @@ public void closeWaitsForConnectionToBeReleasedBeforeClosingConnectionPool() thr @Test public void acquireAfterCloseFails() throws InterruptedException { - ChannelPool connectionPool = Mockito.mock(ChannelPool.class); - - Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup.next(), 2, Collections.emptyList()); + ChannelPool connectionPool = mock(ChannelPool.class); + Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup.next(), null); h2Pool.close(); - assertThat(h2Pool.acquire().await().isSuccess()).isFalse(); + Future acquireResult = h2Pool.acquire().await(); + assertThat(acquireResult.isSuccess()).isFalse(); + assertThat(acquireResult.cause()).isInstanceOf(IOException.class); + } + + @Test + public void closeWaitsForConnectionToBeReleasedBeforeClosingConnectionPool() { + SocketChannel channel = new NioSocketChannel(); + try { + loopGroup.register(channel).awaitUninterruptibly(); + + ChannelPool connectionPool = mock(ChannelPool.class); + ArgumentCaptor releasePromise = ArgumentCaptor.forClass(Promise.class); + when(connectionPool.release(eq(channel), releasePromise.capture())).thenAnswer(invocation -> { + Promise promise = releasePromise.getValue(); + promise.setSuccess(null); + return promise; + }); + + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 8, null); + Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup, + Collections.singleton(record), null); + + h2Pool.close(); + + InOrder inOrder = Mockito.inOrder(connectionPool); + inOrder.verify(connectionPool).release(eq(channel), isA(Promise.class)); + inOrder.verify(connectionPool).close(); + } finally { + channel.close().awaitUninterruptibly(); + } + } + + @Test + public void acquire_shouldAcquireAgainIfExistingNotReusable() throws Exception { + Channel channel = new EmbeddedChannel(); + + try { + ChannelPool connectionPool = Mockito.mock(ChannelPool.class); + + loopGroup.register(channel).awaitUninterruptibly(); + Promise channelPromise = new DefaultPromise<>(loopGroup.next()); + channelPromise.setSuccess(channel); + + Mockito.when(connectionPool.acquire()).thenReturn(channelPromise); + + Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup, + Collections.emptySet(), null); + + h2Pool.acquire().awaitUninterruptibly(); + h2Pool.acquire().awaitUninterruptibly(); + + Mockito.verify(connectionPool, Mockito.times(2)).acquire(); + } finally { + channel.close(); + } } @Test(timeout = 5_000) @@ -99,17 +180,14 @@ public void interruptDuringClosePreservesFlag() throws InterruptedException { Promise channelPromise = new DefaultPromise<>(loopGroup.next()); channelPromise.setSuccess(channel); - ChannelPool connectionPool = Mockito.mock(ChannelPool.class); + ChannelPool connectionPool = mock(ChannelPool.class); Promise releasePromise = Mockito.spy(new DefaultPromise<>(loopGroup.next())); - Mockito.when(connectionPool.release(Mockito.eq(channel))).thenReturn(releasePromise); + when(connectionPool.release(eq(channel))).thenReturn(releasePromise); - MultiplexedChannelRecord record = new MultiplexedChannelRecord(channelPromise, - channel, - 8, - (ch, rec) -> { - }); - Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup.next(), 2, Collections.singletonList(record)); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 8, null); + Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup, + Collections.singleton(record), null); CompletableFuture interrupteFlagPreserved = new CompletableFuture<>(); @@ -131,4 +209,53 @@ public void interruptDuringClosePreservesFlag() throws InterruptedException { channel.close().awaitUninterruptibly(); } } + + @Test + public void acquire_shouldExpandConnectionWindowSizeProportionally() { + int maxConcurrentStream = 3; + EmbeddedChannel channel = newHttp2Channel(); + channel.attr(ChannelAttributeKey.MAX_CONCURRENT_STREAMS).set((long) maxConcurrentStream); + + try { + ChannelPool connectionPool = Mockito.mock(ChannelPool.class); + + loopGroup.register(channel).awaitUninterruptibly(); + Promise channelPromise = new DefaultPromise<>(loopGroup.next()); + channelPromise.setSuccess(channel); + + Mockito.when(connectionPool.acquire()).thenReturn(channelPromise); + + Http2MultiplexedChannelPool h2Pool = new Http2MultiplexedChannelPool(connectionPool, loopGroup, + Collections.emptySet(), null); + + Future acquire = h2Pool.acquire(); + acquire.awaitUninterruptibly(); + channel.runPendingTasks(); + + Http2Connection http2Connection = channel.attr(HTTP2_CONNECTION).get(); + Http2LocalFlowController flowController = + http2Connection.local().flowController(); + + System.out.println(flowController.initialWindowSize()); + Http2Stream connectionStream = http2Connection.stream(0); + + // 1_048_576 (initial configured window size), 65535 (configured initial window size) + // (1048576 - 65535) *2 + 65535 = 2031617 + assertThat(flowController.windowSize(connectionStream)).isEqualTo(2031617); + + // 2031617 + 1048576 (configured initial window size) = 3080193 + assertThat(flowController.initialWindowSize(connectionStream)).isEqualTo(3080193); + + // acquire again + h2Pool.acquire().awaitUninterruptibly(); + channel.runPendingTasks(); + + // 3080193 + 1048576 (configured initial window size) = 4128769 + assertThat(flowController.initialWindowSize(connectionStream)).isEqualTo(4128769); + + Mockito.verify(connectionPool, Mockito.times(1)).acquire(); + } finally { + channel.close(); + } + } } diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2PingHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2PingHandlerTest.java new file mode 100644 index 000000000000..2788337dacba --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2PingHandlerTest.java @@ -0,0 +1,242 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static java.time.temporal.ChronoUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http2.DefaultHttp2PingFrame; +import io.netty.handler.codec.http2.Http2PingFrame; +import java.io.IOException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; + +public class Http2PingHandlerTest { + private static final int FAST_CHECKER_DURATION_MILLIS = 100; + + private Http2PingHandler fastChecker; + private Http2PingHandler slowChecker; + + @Before + public void setup() throws Exception { + this.fastChecker = new Http2PingHandler(FAST_CHECKER_DURATION_MILLIS); + this.slowChecker = new Http2PingHandler(30 * 1_000); + } + + @Test + public void register_withoutProtocol_Fails() { + EmbeddedChannel channel = new EmbeddedChannel(slowChecker); + assertThat(channel.pipeline().get(Http2PingHandler.class)).isNull(); + } + + @Test + public void register_withIncompleteProtocol_doesNotPing() { + EmbeddedChannel channel = createChannelWithoutProtocol(fastChecker); + channel.runPendingTasks(); + + DefaultHttp2PingFrame sentFrame = channel.readOutbound(); + + assertThat(sentFrame).isNull(); + } + + @Test + public void register_withHttp1Protocol_doesNotPing() { + EmbeddedChannel channel = createHttp1Channel(fastChecker); + channel.runPendingTasks(); + + DefaultHttp2PingFrame sentFrame = channel.readOutbound(); + + assertThat(sentFrame).isNull(); + } + + @Test + public void register_WithHttp2Protocol_pingsImmediately() { + EmbeddedChannel channel = createHttp2Channel(slowChecker); + channel.runPendingTasks(); + + DefaultHttp2PingFrame sentFrame = channel.readOutbound(); + + assertThat(sentFrame).isNotNull(); + assertThat(sentFrame.ack()).isFalse(); + } + + @Test + public void unregister_stopsRunning() throws InterruptedException { + EmbeddedChannel channel = createHttp2Channel(fastChecker); + channel.pipeline().remove(Http2PingHandler.class); + + // Flush out any tasks that happened before we closed + channel.runPendingTasks(); + + while (channel.readOutbound() != null) { + // Discard + } + + Thread.sleep(FAST_CHECKER_DURATION_MILLIS); + + DefaultHttp2PingFrame sentFrame = channel.readOutbound(); + + assertThat(sentFrame).isNull(); + } + + @Test + public void ignoredPingsResultInOneChannelException() throws InterruptedException { + PipelineExceptionCatcher catcher = new PipelineExceptionCatcher(); + EmbeddedChannel channel = createHttp2Channel(fastChecker, catcher); + + Thread.sleep(FAST_CHECKER_DURATION_MILLIS); + channel.runPendingTasks(); + + assertThat(catcher.caughtExceptions).hasSize(1); + assertThat(catcher.caughtExceptions.get(0)).isInstanceOf(IOException.class); + } + + @Test + public void respondedToPingsResultInNoAction() { + PipelineExceptionCatcher catcher = new PipelineExceptionCatcher(); + EmbeddedChannel channel = createHttp2Channel(fastChecker, catcher); + + channel.eventLoop().scheduleAtFixedRate(() -> channel.writeInbound(new DefaultHttp2PingFrame(0, true)), + 0, FAST_CHECKER_DURATION_MILLIS, TimeUnit.MILLISECONDS); + + Instant runEnd = Instant.now().plus(1, SECONDS); + while (Instant.now().isBefore(runEnd)) { + channel.runPendingTasks(); + } + + assertThat(catcher.caughtExceptions).isEmpty(); + } + + @Test + public void nonAckPingsResultInOneChannelException() { + PipelineExceptionCatcher catcher = new PipelineExceptionCatcher(); + EmbeddedChannel channel = createHttp2Channel(fastChecker, catcher); + + channel.eventLoop().scheduleAtFixedRate(() -> channel.writeInbound(new DefaultHttp2PingFrame(0, false)), + 0, FAST_CHECKER_DURATION_MILLIS, TimeUnit.MILLISECONDS); + + Instant runEnd = Instant.now().plus(1, SECONDS); + while (Instant.now().isBefore(runEnd)) { + channel.runPendingTasks(); + } + + assertThat(catcher.caughtExceptions).hasSize(1); + assertThat(catcher.caughtExceptions.get(0)).isInstanceOf(IOException.class); + } + + @Test + public void failedWriteResultsInOneChannelException() throws InterruptedException { + PipelineExceptionCatcher catcher = new PipelineExceptionCatcher(); + EmbeddedChannel channel = createHttp2Channel(fastChecker, catcher, new FailingWriter()); + channel.runPendingTasks(); + assertThat(catcher.caughtExceptions).hasSize(1); + assertThat(catcher.caughtExceptions.get(0)).isInstanceOf(IOException.class); + } + + @Test + public void ackPingsAreNotForwardedToOtherHandlers() throws InterruptedException { + PingReadCatcher catcher = new PingReadCatcher(); + EmbeddedChannel channel = createHttp2Channel(fastChecker, catcher); + channel.writeInbound(new DefaultHttp2PingFrame(0, true)); + + channel.runPendingTasks(); + + assertThat(catcher.caughtPings).isEmpty(); + } + + private static EmbeddedChannel createChannelWithoutProtocol(ChannelHandler... handlers) { + EmbeddedChannel channel = new EmbeddedChannel(); + channel.attr(ChannelAttributeKey.PROTOCOL_FUTURE).set(new CompletableFuture<>()); + channel.pipeline().addLast(handlers); + return channel; + } + + private static EmbeddedChannel createHttp1Channel(ChannelHandler... handlers) { + EmbeddedChannel channel = createChannelWithoutProtocol(handlers); + channel.attr(ChannelAttributeKey.PROTOCOL_FUTURE).get().complete(Protocol.HTTP1_1); + return channel; + } + + private static EmbeddedChannel createHttp2Channel(ChannelHandler... handlers) { + EmbeddedChannel channel = createChannelWithoutProtocol(handlers); + channel.attr(ChannelAttributeKey.PROTOCOL_FUTURE).get().complete(Protocol.HTTP2); + return channel; + } + + @Test + public void nonAckPingsAreForwardedToOtherHandlers() throws InterruptedException { + PingReadCatcher catcher = new PingReadCatcher(); + EmbeddedChannel channel = createHttp2Channel(fastChecker, catcher); + channel.writeInbound(new DefaultHttp2PingFrame(0, false)); + + channel.runPendingTasks(); + + assertThat(catcher.caughtPings).hasSize(1); + } + + @Test + public void channelInactive_shouldCancelTaskAndForwardToOtherHandlers() { + EmbeddedChannel channel = createHttp2Channel(fastChecker); + ChannelHandlerContext context = Mockito.mock(ChannelHandlerContext.class); + fastChecker.channelInactive(context); + Mockito.verify(context).fireChannelInactive(); + + channel.writeInbound(new DefaultHttp2PingFrame(0, false)); + assertThat(channel.runScheduledPendingTasks()).isEqualTo(-1L); + } + + private static final class PingReadCatcher extends SimpleChannelInboundHandler { + private final List caughtPings = Collections.synchronizedList(new ArrayList<>()); + + @Override + protected void channelRead0(ChannelHandlerContext ctx, Http2PingFrame msg) { + caughtPings.add(msg); + } + } + + private static final class PipelineExceptionCatcher extends ChannelInboundHandlerAdapter { + private final List caughtExceptions = Collections.synchronizedList(new ArrayList<>()); + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + caughtExceptions.add(cause); + super.exceptionCaught(ctx, cause); + } + } + + private static final class FailingWriter extends ChannelOutboundHandlerAdapter { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + promise.setFailure(new IOException("Failed!")); + } + } +} \ No newline at end of file diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandlerTest.java index 8db3fa23b7c6..fafdf215685c 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandlerTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2SettingsFrameHandlerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_POOL_RECORD; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.MAX_CONCURRENT_STREAMS; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; @@ -92,7 +92,7 @@ public void channelRead_useClientMaxStreams() { } @Test - public void exceptionCaught_shouldHandleErrorCloseChannel() throws InterruptedException { + public void exceptionCaught_shouldHandleErrorCloseChannel() throws Exception { Throwable cause = new Throwable(new RuntimeException("BOOM")); handler.exceptionCaught(context, cause); verifyChannelError(cause.getClass()); @@ -105,7 +105,7 @@ public void channelUnregistered_ProtocolFutureNotDone_ShouldRaiseError() throws } private void verifyChannelError(Class cause) throws InterruptedException { - channel.attr(CHANNEL_POOL_RECORD).set(null); + channel.attr(HTTP2_MULTIPLEXED_CHANNEL_POOL).set(null); channel.runAllPendingTasks(); diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2StreamExceptionHandlerTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2StreamExceptionHandlerTest.java new file mode 100644 index 000000000000..6eac08f8269d --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/Http2StreamExceptionHandlerTest.java @@ -0,0 +1,122 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PING_TRACKER; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.handler.timeout.ReadTimeoutException; +import java.io.IOException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.runners.MockitoJUnitRunner; +import software.amazon.awssdk.http.nio.netty.internal.MockChannel; + +@RunWith(MockitoJUnitRunner.class) +public class Http2StreamExceptionHandlerTest { + + private static final NioEventLoopGroup GROUP = new NioEventLoopGroup(1); + private Http2StreamExceptionHandler handler; + + @Mock + private ChannelHandlerContext context; + + @Mock + private Channel mockParentChannel; + + private MockChannel embeddedParentChannel; + + @Mock + private Channel streamChannel; + + private TestVerifyExceptionHandler verifyExceptionHandler; + + + @Before + public void setup() throws Exception { + embeddedParentChannel = new MockChannel(); + verifyExceptionHandler = new TestVerifyExceptionHandler(); + embeddedParentChannel.pipeline().addLast(verifyExceptionHandler); + when(context.channel()).thenReturn(streamChannel); + handler = Http2StreamExceptionHandler.create(); + when(context.executor()).thenReturn(GROUP.next()); + } + + + @After + public void tearDown() { + embeddedParentChannel.close().awaitUninterruptibly(); + Mockito.reset(streamChannel, context, mockParentChannel); + } + + @AfterClass + public static void teardown() { + GROUP.shutdownGracefully().awaitUninterruptibly(); + } + + @Test + public void timeoutException_shouldFireExceptionAndPropagateException() { + when(streamChannel.parent()).thenReturn(embeddedParentChannel); + handler.exceptionCaught(context, ReadTimeoutException.INSTANCE); + + assertThat(verifyExceptionHandler.exceptionCaught).isExactlyInstanceOf(Http2StreamExceptionHandler.Http2StreamIoException.class); + verify(context).fireExceptionCaught(ReadTimeoutException.INSTANCE); + } + + @Test + public void ioException_shouldFireExceptionAndPropagateException() { + IOException ioException = new IOException("yolo"); + when(streamChannel.parent()).thenReturn(embeddedParentChannel); + handler.exceptionCaught(context, ioException); + + assertThat(verifyExceptionHandler.exceptionCaught).isExactlyInstanceOf(Http2StreamExceptionHandler.Http2StreamIoException.class); + verify(context).fireExceptionCaught(ioException); + } + + @Test + public void otherException_shouldJustPropagateException() { + when(streamChannel.parent()).thenReturn(embeddedParentChannel); + + RuntimeException otherException = new RuntimeException("test"); + handler.exceptionCaught(context, otherException); + + assertThat(embeddedParentChannel.attr(PING_TRACKER).get()).isNull(); + + verify(context).fireExceptionCaught(otherException); + assertThat(verifyExceptionHandler.exceptionCaught).isNull(); + } + + private static final class TestVerifyExceptionHandler extends ChannelInboundHandlerAdapter { + private Throwable exceptionCaught; + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + exceptionCaught = cause; + } + } +} \ No newline at end of file diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPoolTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPoolTest.java index a52ddb8a1922..bd2728465c76 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPoolTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpOrHttp2ChannelPoolTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,6 +15,14 @@ package software.amazon.awssdk.http.nio.netty.internal.http2; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT; +import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_PENDING_CONNECTION_ACQUIRES; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; + import io.netty.channel.Channel; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; @@ -22,6 +30,8 @@ import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -30,20 +40,10 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.MockChannel; import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration; import software.amazon.awssdk.utils.AttributeMap; -import java.time.Duration; -import java.util.concurrent.CompletableFuture; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.CONNECTION_ACQUIRE_TIMEOUT; -import static software.amazon.awssdk.http.SdkHttpConfigurationOption.MAX_PENDING_CONNECTION_ACQUIRES; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.PROTOCOL_FUTURE; - /** * Tests for {@link HttpOrHttp2ChannelPool}. */ @@ -82,6 +82,34 @@ public void protocolConfigNotStarted_closeSucceeds() { httpOrHttp2ChannelPool.close(); } + @Test(timeout = 5_000) + public void invalidProtocolConfig_shouldFailPromise() throws Exception { + HttpOrHttp2ChannelPool invalidChannelPool = new HttpOrHttp2ChannelPool(mockDelegatePool, + eventLoopGroup, + 4, + new NettyConfiguration(AttributeMap.builder() + .put(CONNECTION_ACQUIRE_TIMEOUT, Duration.ofSeconds(1)) + .put(MAX_PENDING_CONNECTION_ACQUIRES, 0) + .build())); + + Promise acquirePromise = eventLoopGroup.next().newPromise(); + when(mockDelegatePool.acquire()).thenReturn(acquirePromise); + + Thread.sleep(500); + + Channel channel = new MockChannel(); + eventLoopGroup.register(channel); + + channel.attr(PROTOCOL_FUTURE).set(CompletableFuture.completedFuture(Protocol.HTTP1_1)); + + acquirePromise.setSuccess(channel); + + Future p = invalidChannelPool.acquire(); + assertThat(p.await().cause().getMessage()).contains("maxPendingAcquires: 0 (expected: >= 1)"); + verify(mockDelegatePool).release(channel); + assertThat(channel.isOpen()).isFalse(); + } + @Test public void protocolConfigNotStarted_closeClosesDelegatePool() throws InterruptedException { httpOrHttp2ChannelPool.close(); diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapterTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapterTest.java new file mode 100644 index 000000000000..7e75717465f4 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/HttpToHttp2OutboundAdapterTest.java @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import io.netty.channel.DefaultChannelPromise; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.handler.codec.http2.HttpConversionUtil; +import java.util.List; +import org.junit.AfterClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class HttpToHttp2OutboundAdapterTest { + private static final NioEventLoopGroup EVENT_LOOP_GROUP = new NioEventLoopGroup(1); + + @Mock + public ChannelHandlerContext ctx; + + @Mock + public Channel channel; + + @AfterClass + public static void classTeardown() { + EVENT_LOOP_GROUP.shutdownGracefully(); + } + + @Test + public void aggregatesWritePromises() { + when(ctx.executor()).thenReturn(EVENT_LOOP_GROUP.next()); + when(ctx.channel()).thenReturn(channel); + + HttpToHttp2OutboundAdapter adapter = new HttpToHttp2OutboundAdapter(); + ChannelPromise writePromise = new DefaultChannelPromise(channel, EVENT_LOOP_GROUP.next()); + + writeRequest(adapter, writePromise); + + ArgumentCaptor writePromiseCaptor = ArgumentCaptor.forClass(ChannelPromise.class); + verify(ctx, atLeastOnce()).write(any(Object.class), writePromiseCaptor.capture()); + + List writePromises = writePromiseCaptor.getAllValues(); + + assertThat(writePromise.isDone()).isFalse(); + + writePromises.forEach(ChannelPromise::setSuccess); + + assertThat(writePromise.isDone()).isTrue(); + } + + private void writeRequest(HttpToHttp2OutboundAdapter adapter, ChannelPromise promise) { + FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, "/", Unpooled.wrappedBuffer(new byte[16])); + request.headers().add(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); + adapter.write(ctx, request, promise); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecordTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecordTest.java new file mode 100644 index 000000000000..28a54eefc024 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/MultiplexedChannelRecordTest.java @@ -0,0 +1,259 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.Promise; +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; +import software.amazon.awssdk.http.nio.netty.internal.MockChannel; +import software.amazon.awssdk.http.nio.netty.internal.UnusedChannelExceptionHandler; + +public class MultiplexedChannelRecordTest { + private EventLoopGroup loopGroup; + private MockChannel channel; + + @Before + public void setup() throws Exception { + loopGroup = new NioEventLoopGroup(4); + channel = new MockChannel(); + } + + @After + public void teardown() { + loopGroup.shutdownGracefully().awaitUninterruptibly(); + channel.close(); + } + + @Test + public void nullIdleTimeoutSeemsToDisableReaping() throws InterruptedException { + EmbeddedChannel channel = newHttp2Channel(); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 1, null); + + Promise streamPromise = channel.eventLoop().newPromise(); + record.acquireStream(streamPromise); + + channel.runPendingTasks(); + + assertThat(streamPromise.isSuccess()).isTrue(); + assertThat(channel.isOpen()).isTrue(); + + record.closeAndReleaseChild(streamPromise.getNow()); + + assertThat(channel.isOpen()).isTrue(); + + Thread.sleep(1_000); + channel.runPendingTasks(); + + assertThat(channel.isOpen()).isTrue(); + } + + @Test + public void recordsWithoutReservedStreamsAreClosedAfterTimeout() throws InterruptedException { + int idleTimeoutMillis = 1000; + EmbeddedChannel channel = newHttp2Channel(); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 1, Duration.ofMillis(idleTimeoutMillis)); + + Promise streamPromise = channel.eventLoop().newPromise(); + record.acquireStream(streamPromise); + + channel.runPendingTasks(); + + assertThat(streamPromise.isSuccess()).isTrue(); + assertThat(channel.isOpen()).isTrue(); + + record.closeAndReleaseChild(streamPromise.getNow()); + + assertThat(channel.isOpen()).isTrue(); + + Thread.sleep(idleTimeoutMillis * 2); + channel.runPendingTasks(); + + assertThat(channel.isOpen()).isFalse(); + } + + @Test + public void recordsWithReservedStreamsAreNotClosedAfterTimeout() throws InterruptedException { + int idleTimeoutMillis = 1000; + EmbeddedChannel channel = newHttp2Channel(); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 2, Duration.ofMillis(idleTimeoutMillis)); + + Promise streamPromise = channel.eventLoop().newPromise(); + Promise streamPromise2 = channel.eventLoop().newPromise(); + record.acquireStream(streamPromise); + record.acquireStream(streamPromise2); + + channel.runPendingTasks(); + + assertThat(streamPromise.isSuccess()).isTrue(); + assertThat(streamPromise2.isSuccess()).isTrue(); + assertThat(channel.isOpen()).isTrue(); + + record.closeAndReleaseChild(streamPromise.getNow()); + + assertThat(channel.isOpen()).isTrue(); + + Thread.sleep(idleTimeoutMillis * 2); + channel.runPendingTasks(); + + assertThat(channel.isOpen()).isTrue(); + } + + @Test + public void acquireRequestResetsCloseTimer() throws InterruptedException { + int idleTimeoutMillis = 1000; + EmbeddedChannel channel = newHttp2Channel(); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 2, Duration.ofMillis(idleTimeoutMillis)); + + for (int i = 0; i < 20; ++i) { + Thread.sleep(idleTimeoutMillis / 10); + channel.runPendingTasks(); + + Promise streamPromise = channel.eventLoop().newPromise(); + assertThat(record.acquireStream(streamPromise)).isTrue(); + channel.runPendingTasks(); + + assertThat(streamPromise.isSuccess()).isTrue(); + assertThat(channel.isOpen()).isTrue(); + + record.closeAndReleaseChild(streamPromise.getNow()); + channel.runPendingTasks(); + } + + assertThat(channel.isOpen()).isTrue(); + + Thread.sleep(idleTimeoutMillis * 2); + channel.runPendingTasks(); + + assertThat(channel.isOpen()).isFalse(); + } + + @Test + public void idleTimerDoesNotApplyBeforeFirstChannelIsCreated() throws InterruptedException { + int idleTimeoutMillis = 1000; + EmbeddedChannel channel = newHttp2Channel(); + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 2, Duration.ofMillis(idleTimeoutMillis)); + + Thread.sleep(idleTimeoutMillis * 2); + channel.runPendingTasks(); + + assertThat(channel.isOpen()).isTrue(); + } + + @Test + public void availableStream0_reusableShouldBeFalse() { + loopGroup.register(channel).awaitUninterruptibly(); + Promise channelPromise = new DefaultPromise<>(loopGroup.next()); + channelPromise.setSuccess(channel); + + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 0, Duration.ofSeconds(10)); + + assertThat(record.acquireStream(null)).isFalse(); + } + + @Test + public void acquireClaimedConnection_channelClosed_shouldThrowIOException() { + loopGroup.register(channel).awaitUninterruptibly(); + Promise channelPromise = new DefaultPromise<>(loopGroup.next()); + + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 1, Duration.ofSeconds(10)); + + record.closeChildChannels(); + + record.acquireClaimedStream(channelPromise); + + assertThatThrownBy(() -> channelPromise.get()).hasCauseInstanceOf(IOException.class); + } + + @Test + public void closeChildChannels_shouldDeliverException() throws ExecutionException, InterruptedException { + EmbeddedChannel channel = newHttp2Channel(); + loopGroup.register(channel).awaitUninterruptibly(); + Promise channelPromise = new DefaultPromise<>(loopGroup.next()); + channelPromise.setSuccess(channel); + + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 2, Duration.ofSeconds(10)); + + Promise streamPromise = channel.eventLoop().newPromise(); + record.acquireStream(streamPromise); + + channel.runPendingTasks(); + Channel childChannel = streamPromise.get(); + VerifyExceptionHandler verifyExceptionHandler = new VerifyExceptionHandler(); + childChannel.pipeline().addFirst(verifyExceptionHandler); + + IOException ioException = new IOException("foobar"); + record.closeChildChannels(ioException); + + assertThat(childChannel.pipeline().get(UnusedChannelExceptionHandler.class)).isNotNull(); + + assertThat(verifyExceptionHandler.exceptionCaught).hasStackTraceContaining("foobar") + .hasRootCauseInstanceOf(IOException.class); + + // should be closed by UnusedChannelExceptionHandler + assertThat(childChannel.isOpen()).isFalse(); + } + + @Test + public void closeToNewStreams_AcquireStreamShouldReturnFalse() { + MultiplexedChannelRecord record = new MultiplexedChannelRecord(channel, 2, Duration.ofSeconds(10)); + Promise streamPromise = channel.eventLoop().newPromise(); + assertThat(record.acquireStream(streamPromise)).isTrue(); + + record.closeToNewStreams(); + assertThat(record.acquireStream(streamPromise)).isFalse(); + } + + private static final class VerifyExceptionHandler extends ChannelInboundHandlerAdapter { + private Throwable exceptionCaught; + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { + exceptionCaught = cause; + ctx.fireExceptionCaught(cause); + } + } + + private EmbeddedChannel newHttp2Channel() { + EmbeddedChannel channel = new EmbeddedChannel(Http2FrameCodecBuilder.forClient().build(), + new Http2MultiplexHandler(new NoOpHandler())); + channel.attr(ChannelAttributeKey.PROTOCOL_FUTURE).set(CompletableFuture.completedFuture(Protocol.HTTP2)); + return channel; + } + + private static class NoOpHandler extends ChannelInitializer { + @Override + protected void initChannel(Channel ch) { } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/ReadTimeoutTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/ReadTimeoutTest.java new file mode 100644 index 000000000000..f1d66aa9f726 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/ReadTimeoutTest.java @@ -0,0 +1,214 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.DefaultHttp2WindowUpdateFrame; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Frame; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2HeadersFrame; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.util.ReferenceCountUtil; +import io.reactivex.Flowable; +import java.nio.ByteBuffer; +import java.time.Duration; +import java.util.Optional; +import java.util.function.Supplier; +import org.junit.After; +import org.junit.Test; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; +import software.amazon.awssdk.http.async.SdkHttpContentPublisher; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; + +public class ReadTimeoutTest { + private static final int N_FRAMES = 10; + private TestH2Server testServer; + private SdkAsyncHttpClient netty; + + @After + public void methodTeardown() throws InterruptedException { + if (testServer != null) { + testServer.shutdown(); + } + testServer = null; + + if (netty != null) { + netty.close(); + } + netty = null; + } + + @Test + public void readTimeoutActivatedAfterRequestFullyWritten() throws InterruptedException { + testServer = new TestH2Server(StreamHandler::new); + testServer.init(); + + // Set a very short read timeout, shorter than it will take to transfer + // the body + netty = NettyNioAsyncHttpClient.builder() + .protocol(Protocol.HTTP2) + .readTimeout(Duration.ofMillis(500)) + .build(); + + SdkHttpFullRequest sdkRequest = SdkHttpFullRequest.builder() + .method(SdkHttpMethod.PUT) + .protocol("http") + .host("localhost") + .port(testServer.port()) + .build(); + + // at 10 frames, should take approximately 3-4 seconds for the server + // to receive given that it sleeps for 500ms between data frames and + // sleeps for most of them + byte[] data = new byte[16384 * N_FRAMES]; + + Publisher dataPublisher = Flowable.just(ByteBuffer.wrap(data)); + + AsyncExecuteRequest executeRequest = AsyncExecuteRequest.builder() + .request(sdkRequest) + .responseHandler(new SdkAsyncHttpResponseHandler() { + @Override + public void onHeaders(SdkHttpResponse headers) { + } + + @Override + public void onStream(Publisher stream) { + Flowable.fromPublisher(stream).forEach(s -> {}); + } + + @Override + public void onError(Throwable error) { + } + }) + .requestContentPublisher(new SdkHttpContentPublisher() { + @Override + public Optional contentLength() { + return Optional.of((long) data.length); + } + + @Override + public void subscribe(Subscriber s) { + dataPublisher.subscribe(s); + } + }) + .build(); + + netty.execute(executeRequest).join(); + } + + private static final class TestH2Server extends ChannelInitializer { + private final Supplier handlerSupplier; + + private ServerBootstrap bootstrap; + private ServerSocketChannel channel; + + private TestH2Server(Supplier handlerSupplier) { + this.handlerSupplier = handlerSupplier; + } + + public void init() throws InterruptedException { + bootstrap = new ServerBootstrap() + .channel(NioServerSocketChannel.class) + .group(new NioEventLoopGroup()) + .childHandler(this) + .localAddress(0) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + channel = ((ServerSocketChannel) bootstrap.bind().await().channel()); + } + + public int port() { + return channel.localAddress().getPort(); + } + + public void shutdown() throws InterruptedException { + channel.close().await(); + } + + @Override + protected void initChannel(SocketChannel ch) { + Http2FrameCodec codec = Http2FrameCodecBuilder.forServer() + .autoAckPingFrame(true) + .initialSettings(new Http2Settings() + .initialWindowSize(16384) + .maxFrameSize(16384) + .maxConcurrentStreams(5)) + .build(); + + ch.pipeline().addLast(codec); + ch.pipeline().addLast(handlerSupplier.get()); + } + } + + private static class StreamHandler extends ChannelInboundHandlerAdapter { + private int sleeps = N_FRAMES - 3; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (!(msg instanceof Http2Frame)) { + ctx.fireChannelRead(msg); + return; + } + + Http2Frame frame = (Http2Frame) msg; + if (frame instanceof Http2DataFrame) { + Http2DataFrame dataFrame = (Http2DataFrame) frame; + ReferenceCountUtil.release(frame); + if (dataFrame.isEndStream()) { + Http2HeadersFrame respHeaders = new DefaultHttp2HeadersFrame( + new DefaultHttp2Headers().status("204"), true) + .stream(dataFrame.stream()); + ctx.writeAndFlush(respHeaders); + } + + if (sleeps > 0) { + --sleeps; + // Simulate a server that's slow to read data. Since our + // window size is equal to the max frame size, the client + // shouldn't be able to send more data until we update our + // window + try { + Thread.sleep(500); + } catch (InterruptedException ie) { + } + } + ctx.writeAndFlush(new DefaultHttp2WindowUpdateFrame(dataFrame.initialFlowControlledBytes()) + .stream(dataFrame.stream())); + } + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/WindowSizeTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/WindowSizeTest.java new file mode 100644 index 000000000000..7210708d7b59 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/WindowSizeTest.java @@ -0,0 +1,286 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2; + +import static org.assertj.core.api.Assertions.assertThat; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.ServerSocketChannel; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.http2.DefaultHttp2Headers; +import io.netty.handler.codec.http2.DefaultHttp2HeadersFrame; +import io.netty.handler.codec.http2.Http2DataFrame; +import io.netty.handler.codec.http2.Http2Frame; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2HeadersFrame; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.codec.http2.Http2SettingsFrame; +import io.netty.util.ReferenceCountUtil; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.reactivestreams.Publisher; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.http.async.AsyncExecuteRequest; +import software.amazon.awssdk.http.async.SdkAsyncHttpClient; +import software.amazon.awssdk.http.async.SdkAsyncHttpResponseHandler; +import software.amazon.awssdk.http.nio.netty.EmptyPublisher; +import software.amazon.awssdk.http.nio.netty.Http2Configuration; +import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; + +public class WindowSizeTest { + private static final int DEFAULT_INIT_WINDOW_SIZE = 1024 * 1024; + + private TestH2Server server; + private SdkAsyncHttpClient netty; + + @Rule + public ExpectedException expected = ExpectedException.none(); + + @After + public void methodTeardown() throws InterruptedException { + if (netty != null) { + netty.close(); + } + netty = null; + + if (server != null) { + server.shutdown(); + } + server = null; + } + + @Test + public void builderSetter_negativeValue_throws() { + expected.expect(IllegalArgumentException.class); + + NettyNioAsyncHttpClient.builder() + .http2Configuration(Http2Configuration.builder() + .initialWindowSize(-1) + .build()) + .build(); + } + + @Test + public void builderSetter_0Value_throws() { + expected.expect(IllegalArgumentException.class); + + NettyNioAsyncHttpClient.builder() + .http2Configuration(Http2Configuration.builder() + .initialWindowSize(0) + .build()) + .build(); + } + + @Test + public void builderSetter_explicitNullSet_usesDefaultValue() throws InterruptedException { + expectCorrectWindowSizeValueTest(null, DEFAULT_INIT_WINDOW_SIZE); + } + + @Test + public void execute_customWindowValue_valueSentInSettings() throws InterruptedException { + int windowSize = 128 * 1024 * 1024; + expectCorrectWindowSizeValueTest(windowSize, windowSize); + } + + @Test + public void execute_noExplicitValueSet_sendsDefaultValueInSettings() throws InterruptedException { + ConcurrentLinkedQueue receivedFrames = new ConcurrentLinkedQueue<>(); + + server = new TestH2Server(() -> new StreamHandler(receivedFrames)); + + server.init(); + + netty = NettyNioAsyncHttpClient.builder() + .protocol(Protocol.HTTP2) + .build(); + + AsyncExecuteRequest req = AsyncExecuteRequest.builder() + .requestContentPublisher(new EmptyPublisher()) + .request(SdkHttpFullRequest.builder() + .method(SdkHttpMethod.GET) + .protocol("http") + .host("localhost") + .port(server.port()) + .build()) + .responseHandler(new SdkAsyncHttpResponseHandler() { + @Override + public void onHeaders(SdkHttpResponse headers) { + } + + @Override + public void onStream(Publisher stream) { + } + + @Override + public void onError(Throwable error) { + } + }) + .build(); + + netty.execute(req).join(); + + List receivedSettings = receivedFrames.stream() + .filter(f -> f instanceof Http2SettingsFrame) + .map(f -> (Http2SettingsFrame) f) + .map(Http2SettingsFrame::settings) + .collect(Collectors.toList()); + + assertThat(receivedSettings.size()).isGreaterThan(0); + for (Http2Settings s : receivedSettings) { + assertThat(s.initialWindowSize()).isEqualTo(DEFAULT_INIT_WINDOW_SIZE); + } + } + + private void expectCorrectWindowSizeValueTest(Integer builderSetterValue, int settingsFrameValue) throws InterruptedException { + ConcurrentLinkedQueue receivedFrames = new ConcurrentLinkedQueue<>(); + + server = new TestH2Server(() -> new StreamHandler(receivedFrames)); + + server.init(); + + netty = NettyNioAsyncHttpClient.builder() + .protocol(Protocol.HTTP2) + .http2Configuration(Http2Configuration.builder() + .initialWindowSize(builderSetterValue) + .build()) + .build(); + + AsyncExecuteRequest req = AsyncExecuteRequest.builder() + .requestContentPublisher(new EmptyPublisher()) + .request(SdkHttpFullRequest.builder() + .method(SdkHttpMethod.GET) + .protocol("http") + .host("localhost") + .port(server.port()) + .build()) + .responseHandler(new SdkAsyncHttpResponseHandler() { + @Override + public void onHeaders(SdkHttpResponse headers) { + } + + @Override + public void onStream(Publisher stream) { + } + + @Override + public void onError(Throwable error) { + } + }) + .build(); + + netty.execute(req).join(); + + + List receivedSettings = receivedFrames.stream() + .filter(f -> f instanceof Http2SettingsFrame) + .map(f -> (Http2SettingsFrame) f) + .map(Http2SettingsFrame::settings) + .collect(Collectors.toList()); + + assertThat(receivedSettings.size()).isGreaterThan(0); + for (Http2Settings s : receivedSettings) { + assertThat(s.initialWindowSize()).isEqualTo(settingsFrameValue); + } + } + + private static final class TestH2Server extends ChannelInitializer { + private final Supplier handlerSupplier; + + private ServerBootstrap bootstrap; + private ServerSocketChannel channel; + + private TestH2Server(Supplier handlerSupplier) { + this.handlerSupplier = handlerSupplier; + } + + public void init() throws InterruptedException { + bootstrap = new ServerBootstrap() + .channel(NioServerSocketChannel.class) + .group(new NioEventLoopGroup()) + .childHandler(this) + .localAddress(0) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + channel = ((ServerSocketChannel) bootstrap.bind().await().channel()); + } + + public int port() { + return channel.localAddress().getPort(); + } + + public void shutdown() throws InterruptedException { + channel.close().await(); + } + + @Override + protected void initChannel(SocketChannel ch) { + Http2FrameCodec codec = Http2FrameCodecBuilder.forServer() + .initialSettings(new Http2Settings() + .maxConcurrentStreams(5)) + .build(); + + ch.pipeline().addLast(codec); + ch.pipeline().addLast(handlerSupplier.get()); + } + } + + private static class StreamHandler extends ChannelInboundHandlerAdapter { + private final Queue receivedFrames; + + private StreamHandler(Queue receivedFrames) { + this.receivedFrames = receivedFrames; + } + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + if (!(msg instanceof Http2Frame)) { + ctx.fireChannelRead(msg); + return; + } + + Http2Frame frame = (Http2Frame) msg; + receivedFrames.add(frame); + if (frame instanceof Http2DataFrame) { + Http2DataFrame dataFrame = (Http2DataFrame) frame; + if (dataFrame.isEndStream()) { + Http2HeadersFrame respHeaders = new DefaultHttp2HeadersFrame( + new DefaultHttp2Headers().status("204"), true) + .stream(dataFrame.stream()); + ctx.writeAndFlush(respHeaders); + } + } + ReferenceCountUtil.release(frame); + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/utils/Http2TestUtils.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/utils/Http2TestUtils.java new file mode 100644 index 000000000000..b91650047453 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/http2/utils/Http2TestUtils.java @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2.utils; + + +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http2.Http2FrameCodec; +import io.netty.handler.codec.http2.Http2FrameCodecBuilder; +import io.netty.handler.codec.http2.Http2FrameLogger; +import io.netty.handler.codec.http2.Http2MultiplexHandler; +import io.netty.handler.codec.http2.Http2Settings; +import io.netty.handler.logging.LogLevel; +import java.util.concurrent.CompletableFuture; +import software.amazon.awssdk.http.Protocol; +import software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey; + +public final class Http2TestUtils { + public static final int INITIAL_WINDOW_SIZE = 1_048_576; + + public static EmbeddedChannel newHttp2Channel() { + return newHttp2Channel(new NoOpHandler()); + } + + public static EmbeddedChannel newHttp2Channel(ChannelHandler channelHandler) { + Http2FrameCodec http2FrameCodec = Http2FrameCodecBuilder.forClient().initialSettings( + Http2Settings.defaultSettings().initialWindowSize(INITIAL_WINDOW_SIZE)) + .frameLogger(new Http2FrameLogger(LogLevel.DEBUG)).build(); + EmbeddedChannel channel = new EmbeddedChannel(http2FrameCodec, + new Http2MultiplexHandler(channelHandler)); + + channel.attr(ChannelAttributeKey.HTTP2_CONNECTION).set(http2FrameCodec.connection()); + channel.attr(ChannelAttributeKey.HTTP2_INITIAL_WINDOW_SIZE).set(INITIAL_WINDOW_SIZE); + channel.attr(ChannelAttributeKey.PROTOCOL_FUTURE).set(CompletableFuture.completedFuture(Protocol.HTTP2)); + return channel; + } + + private static class NoOpHandler extends ChannelInitializer { + @Override + protected void initChannel(Channel ch) { + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/ChannelPublisherTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/ChannelPublisherTest.java new file mode 100644 index 000000000000..7f8755545108 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/ChannelPublisherTest.java @@ -0,0 +1,184 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoop; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.Promise; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class ChannelPublisherTest { + + private EventLoopGroup group; + private Channel channel; + private Publisher publisher; + private SubscriberProbe subscriber; + + @Before + public void start() throws Exception { + group = new NioEventLoopGroup(); + EventLoop eventLoop = group.next(); + + HandlerPublisher handlerPublisher = new HandlerPublisher<>(eventLoop, Channel.class); + Bootstrap bootstrap = new Bootstrap(); + + bootstrap + .channel(NioServerSocketChannel.class) + .group(eventLoop) + .option(ChannelOption.AUTO_READ, false) + .handler(handlerPublisher) + .localAddress("127.0.0.1", 0); + + channel = bootstrap.bind().await().channel(); + this.publisher = handlerPublisher; + + subscriber = new SubscriberProbe<>(); + } + + @After + public void stop() throws Exception { + channel.unsafe().closeForcibly(); + group.shutdownGracefully(); + } + + @Test + public void test() throws Exception { + publisher.subscribe(subscriber); + Subscription sub = subscriber.takeSubscription(); + + // Try one cycle + sub.request(1); + Socket socket1 = connect(); + receiveConnection(); + readWriteData(socket1, 1); + + // Check back pressure + Socket socket2 = connect(); + subscriber.expectNoElements(); + + // Now request the next connection + sub.request(1); + receiveConnection(); + readWriteData(socket2, 2); + + // Close the channel + channel.close(); + subscriber.expectNoElements(); + subscriber.expectComplete(); + } + + private Socket connect() throws Exception { + InetSocketAddress address = (InetSocketAddress) channel.localAddress(); + return new Socket(address.getAddress(), address.getPort()); + } + + private void readWriteData(Socket socket, int data) throws Exception { + OutputStream os = socket.getOutputStream(); + os.write(data); + os.flush(); + InputStream is = socket.getInputStream(); + int received = is.read(); + socket.close(); + assertEquals(received, data); + } + + private void receiveConnection() throws Exception { + Channel channel = subscriber.take(); + channel.pipeline().addLast(new ChannelInboundHandlerAdapter() { + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + ctx.writeAndFlush(msg); + } + }); + group.register(channel); + } + + private class SubscriberProbe implements Subscriber { + final BlockingQueue subscriptions = new LinkedBlockingQueue<>(); + final BlockingQueue elements = new LinkedBlockingQueue<>(); + final Promise promise = new DefaultPromise<>(group.next()); + + public void onSubscribe(Subscription s) { + subscriptions.add(s); + } + + public void onNext(T t) { + elements.add(t); + } + + public void onError(Throwable t) { + promise.setFailure(t); + } + + public void onComplete() { + promise.setSuccess(null); + } + + Subscription takeSubscription() throws Exception { + Subscription sub = subscriptions.poll(100, TimeUnit.MILLISECONDS); + assertNotNull(sub); + return sub; + } + + T take() throws Exception { + T t = elements.poll(1000, TimeUnit.MILLISECONDS); + assertNotNull(t); + return t; + } + + void expectNoElements() throws Exception { + T t = elements.poll(100, TimeUnit.MILLISECONDS); + assertNull(t); + } + + void expectComplete() throws Exception { + promise.get(100, TimeUnit.MILLISECONDS); + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerPublisherVerificationTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerPublisherVerificationTest.java new file mode 100644 index 000000000000..06199aaa58ca --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerPublisherVerificationTest.java @@ -0,0 +1,159 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.local.LocalChannel; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import org.reactivestreams.Publisher; +import org.reactivestreams.tck.PublisherVerification; +import org.reactivestreams.tck.TestEnvironment; +import org.testng.annotations.AfterClass; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Factory; +import software.amazon.awssdk.http.nio.netty.internal.nrs.util.BatchedProducer; +import software.amazon.awssdk.http.nio.netty.internal.nrs.util.ClosedLoopChannel; +import software.amazon.awssdk.http.nio.netty.internal.nrs.util.ScheduledBatchedProducer; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class HandlerPublisherVerificationTest extends PublisherVerification { + + private final int batchSize; + // The number of elements to publish initially, before the subscriber is received + private final int publishInitial; + // Whether we should use scheduled publishing (with a small delay) + private final boolean scheduled; + + private ScheduledExecutorService executor; + private DefaultEventLoopGroup eventLoop; + + @Factory(dataProvider = "data") + public HandlerPublisherVerificationTest(int batchSize, int publishInitial, boolean scheduled) { + super(new TestEnvironment(200)); + this.batchSize = batchSize; + this.publishInitial = publishInitial; + this.scheduled = scheduled; + } + + @DataProvider + public static Object[][] data() { + final int defaultBatchSize = 3; + final int defaultPublishInitial = 3; + final boolean defaultScheduled = false; + + return new Object[][] { + { defaultBatchSize, defaultPublishInitial, defaultScheduled }, + { 1, defaultPublishInitial, defaultScheduled }, + { defaultBatchSize, 0, defaultScheduled }, + { defaultBatchSize, defaultPublishInitial, true } + }; + } + + // I tried making this before/after class, but encountered a strange error where after 32 publishers were created, + // the following tests complained about the executor being shut down when I registered the channel. Though, it + // doesn't happen if you create 32 publishers in a single test. + @BeforeMethod + public void startEventLoop() { + eventLoop = new DefaultEventLoopGroup(); + } + + @AfterMethod + public void stopEventLoop() { + eventLoop.shutdownGracefully(); + eventLoop = null; + } + + @BeforeClass + public void startExecutor() { + executor = Executors.newSingleThreadScheduledExecutor(); + } + + @AfterClass + public void stopExecutor() { + executor.shutdown(); + } + + @Override + public Publisher createPublisher(final long elements) { + final BatchedProducer out; + if (scheduled) { + out = new ScheduledBatchedProducer(elements, batchSize, publishInitial, executor, 5); + } else { + out = new BatchedProducer(elements, batchSize, publishInitial, executor); + } + + final ClosedLoopChannel channel = new ClosedLoopChannel(); + channel.config().setAutoRead(false); + ChannelFuture registered = eventLoop.register(channel); + + final HandlerPublisher publisher = new HandlerPublisher<>(registered.channel().eventLoop(), Long.class); + + registered.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + channel.pipeline().addLast("out", out); + channel.pipeline().addLast("publisher", publisher); + + for (long i = 0; i < publishInitial && i < elements; i++) { + channel.pipeline().fireChannelRead(i); + } + if (elements <= publishInitial) { + channel.pipeline().fireChannelInactive(); + } + } + }); + + return publisher; + } + + @Override + public Publisher createFailedPublisher() { + LocalChannel channel = new LocalChannel(); + eventLoop.register(channel); + HandlerPublisher publisher = new HandlerPublisher<>(channel.eventLoop(), Long.class); + channel.pipeline().addLast("publisher", publisher); + channel.pipeline().fireExceptionCaught(new RuntimeException("failed")); + + return publisher; + } + + @Override + public void stochastic_spec103_mustSignalOnMethodsSequentially() throws Throwable { + try { + super.stochastic_spec103_mustSignalOnMethodsSequentially(); + } catch (Throwable t) { + // CI is failing here, but maven doesn't tell us which parameters failed + System.out.println("Stochastic test failed with parameters batchSize=" + batchSize + + " publishInitial=" + publishInitial + " scheduled=" + scheduled); + throw t; + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberBlackboxVerificationTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberBlackboxVerificationTest.java new file mode 100644 index 000000000000..69346e06093c --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberBlackboxVerificationTest.java @@ -0,0 +1,98 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandler; +import io.netty.channel.embedded.EmbeddedChannel; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.reactivestreams.tck.SubscriberBlackboxVerification; +import org.reactivestreams.tck.TestEnvironment; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class HandlerSubscriberBlackboxVerificationTest extends SubscriberBlackboxVerification { + + public HandlerSubscriberBlackboxVerificationTest() { + super(new TestEnvironment()); + } + + @Override + public Subscriber createSubscriber() { + // Embedded channel requires at least one handler when it's created, but HandlerSubscriber + // needs the channels event loop in order to be created, so start with a dummy, then replace. + ChannelHandler dummy = new ChannelDuplexHandler(); + EmbeddedChannel channel = new EmbeddedChannel(dummy); + HandlerSubscriber subscriber = new HandlerSubscriber<>(channel.eventLoop(), 2, 4); + channel.pipeline().replace(dummy, "subscriber", subscriber); + + return new SubscriberWithChannel<>(channel, subscriber); + } + + @Override + public Long createElement(int element) { + return (long) element; + } + + @Override + public void triggerRequest(Subscriber subscriber) { + EmbeddedChannel channel = ((SubscriberWithChannel) subscriber).channel; + + channel.runPendingTasks(); + while (channel.readOutbound() != null) { + channel.runPendingTasks(); + } + channel.runPendingTasks(); + } + + /** + * Delegate subscriber that makes the embedded channel available so we can talk to it to trigger a request. + */ + private static class SubscriberWithChannel implements Subscriber { + final EmbeddedChannel channel; + final HandlerSubscriber subscriber; + + public SubscriberWithChannel(EmbeddedChannel channel, HandlerSubscriber subscriber) { + this.channel = channel; + this.subscriber = subscriber; + } + + public void onSubscribe(Subscription s) { + subscriber.onSubscribe(s); + } + + public void onNext(T t) { + subscriber.onNext(t); + } + + public void onError(Throwable t) { + subscriber.onError(t); + } + + public void onComplete() { + subscriber.onComplete(); + } + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberTest.java new file mode 100644 index 000000000000..4b47365fce1f --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberTest.java @@ -0,0 +1,220 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPromise; +import io.netty.channel.DefaultChannelPromise; +import io.netty.channel.EventLoop; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.DefaultFullHttpRequest; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpMethod; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.concurrent.AbstractEventExecutor; +import io.netty.util.concurrent.Future; +import io.netty.util.internal.ObjectUtil; +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.reactivestreams.Subscription; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class HandlerSubscriberTest { + private EmbeddedChannel channel; + private CustomEmbeddedEventLoop eventLoop; + private HandlerSubscriber handler; + + @Before + public void setup() throws Exception { + channel = new CustomEmbeddedChannel(); + eventLoop = new CustomEmbeddedEventLoop(); + eventLoop.register(channel).syncUninterruptibly(); + + handler = new HandlerSubscriber<>(eventLoop); + channel.pipeline().addLast(handler); + } + + @After + public void teardown() { + channel.close(); + } + + /** + * Ensures that onNext invocations against the {@link HandlerSubscriber} do not order things based on which thread is calling + * onNext. + */ + @Test + public void onNextWritesInProperOrderFromAnyThread() { + HttpContent front = emptyHttpRequest(); + HttpContent back = emptyHttpRequest(); + + handler.onSubscribe(doNothingSubscription()); + eventLoop.inEventLoop(false); + handler.onNext(front); + eventLoop.inEventLoop(true); + handler.onNext(back); + + eventLoop.runTasks(); + + Queue outboundMessages = channel.outboundMessages(); + + assertThat(outboundMessages).hasSize(2); + assertThat(outboundMessages.poll()).isSameAs(front); + assertThat(outboundMessages.poll()).isSameAs(back); + } + + private DefaultFullHttpRequest emptyHttpRequest() { + return new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://fake.com"); + } + + private Subscription doNothingSubscription() { + return new Subscription() { + @Override + public void request(long n) { } + + @Override + public void cancel() { } + }; + } + + private static class CustomEmbeddedChannel extends EmbeddedChannel { + private CustomEmbeddedChannel() { + super(false, false); + } + + @Override + protected boolean isCompatible(EventLoop loop) { + return loop instanceof CustomEmbeddedEventLoop; + } + } + + private static class CustomEmbeddedEventLoop extends AbstractEventExecutor implements EventLoop { + private final Queue tasks = new ArrayDeque<>(2); + private volatile boolean inEventLoop = true; + + @Override + public EventLoopGroup parent() { + return (EventLoopGroup) super.parent(); + } + + @Override + public EventLoop next() { + return (EventLoop) super.next(); + } + + @Override + public void execute(Runnable runnable) { + tasks.add(runnable); + } + + public void runTasks() { + for (;;) { + Runnable task = tasks.poll(); + if (task == null) { + break; + } + + task.run(); + } + } + + @Override + public Future shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) { + throw new UnsupportedOperationException(); + } + + @Override + public Future terminationFuture() { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public void shutdown() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isShuttingDown() { + return false; + } + + @Override + public boolean isShutdown() { + return false; + } + + @Override + public boolean isTerminated() { + return false; + } + + @Override + public boolean awaitTermination(long timeout, TimeUnit unit) { + return false; + } + + @Override + public ChannelFuture register(Channel channel) { + return register(new DefaultChannelPromise(channel, this)); + } + + @Override + public ChannelFuture register(ChannelPromise promise) { + ObjectUtil.checkNotNull(promise, "promise"); + promise.channel().unsafe().register(this, promise); + return promise; + } + + @Deprecated + @Override + public ChannelFuture register(Channel channel, ChannelPromise promise) { + channel.unsafe().register(this, promise); + return promise; + } + + public void inEventLoop(boolean inEventLoop) { + this.inEventLoop = inEventLoop; + } + + @Override + public boolean inEventLoop() { + return inEventLoop; + } + + @Override + public boolean inEventLoop(Thread thread) { + return inEventLoop; + } + } +} \ No newline at end of file diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberWhiteboxVerificationTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberWhiteboxVerificationTest.java new file mode 100644 index 000000000000..0a9b7d76aa09 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/HandlerSubscriberWhiteboxVerificationTest.java @@ -0,0 +1,118 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs; + +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.util.concurrent.DefaultPromise; +import io.netty.util.concurrent.Promise; +import org.reactivestreams.Subscriber; +import org.reactivestreams.tck.SubscriberWhiteboxVerification; +import org.reactivestreams.tck.TestEnvironment; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import software.amazon.awssdk.http.nio.netty.internal.nrs.util.ClosedLoopChannel; +import software.amazon.awssdk.http.nio.netty.internal.nrs.util.ProbeHandler; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class HandlerSubscriberWhiteboxVerificationTest extends SubscriberWhiteboxVerification { + + private boolean workAroundIssue277; + + public HandlerSubscriberWhiteboxVerificationTest() { + super(new TestEnvironment()); + } + + private DefaultEventLoopGroup eventLoop; + + // I tried making this before/after class, but encountered a strange error where after 32 publishers were created, + // the following tests complained about the executor being shut down when I registered the channel. Though, it + // doesn't happen if you create 32 publishers in a single test. + @BeforeMethod + public void startEventLoop() { + workAroundIssue277 = false; + eventLoop = new DefaultEventLoopGroup(); + } + + @AfterMethod + public void stopEventLoop() { + eventLoop.shutdownGracefully(); + eventLoop = null; + } + + @Override + public Subscriber createSubscriber(WhiteboxSubscriberProbe probe) { + final ClosedLoopChannel channel = new ClosedLoopChannel(); + channel.config().setAutoRead(false); + ChannelFuture registered = eventLoop.register(channel); + + final HandlerSubscriber subscriber = new HandlerSubscriber<>(registered.channel().eventLoop(), 2, 4); + final ProbeHandler probeHandler = new ProbeHandler<>(probe, Long.class); + final Promise handlersInPlace = new DefaultPromise<>(eventLoop.next()); + + registered.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + channel.pipeline().addLast("probe", probeHandler); + channel.pipeline().addLast("subscriber", subscriber); + handlersInPlace.setSuccess(null); + // Channel needs to be active before the subscriber starts responding to demand + channel.pipeline().fireChannelActive(); + } + }); + + if (workAroundIssue277) { + try { + // Wait for the pipeline to be setup, so we're ready to receive elements even if they aren't requested, + // because https://github.com/reactive-streams/reactive-streams-jvm/issues/277 + handlersInPlace.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + return probeHandler.wrap(subscriber); + } + + @Override + public void required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel() throws Throwable { + // See https://github.com/reactive-streams/reactive-streams-jvm/issues/277 + workAroundIssue277 = true; + super.required_spec208_mustBePreparedToReceiveOnNextSignalsAfterHavingCalledSubscriptionCancel(); + } + + @Override + public void required_spec308_requestMustRegisterGivenNumberElementsToBeProduced() throws Throwable { + workAroundIssue277 = true; + super.required_spec308_requestMustRegisterGivenNumberElementsToBeProduced(); + } + + @Override + public Long createElement(int element) { + return (long) element; + } + +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/BatchedProducer.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/BatchedProducer.java new file mode 100644 index 000000000000..d341d8f850ce --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/BatchedProducer.java @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import java.util.concurrent.Executor; + +/** + * A batched producer. + * + * Responds to read requests with batches of elements according to batch size. When eofOn is reached, it closes the + * channel. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class BatchedProducer extends ChannelOutboundHandlerAdapter { + + protected final long eofOn; + protected final int batchSize; + private final Executor executor; + protected long sequence; + + public BatchedProducer(long eofOn, int batchSize, long sequence, Executor executor) { + this.eofOn = eofOn; + this.batchSize = batchSize; + this.sequence = sequence; + this.executor = executor; + } + + private boolean cancelled = false; + + + @Override + public void read(final ChannelHandlerContext ctx) throws Exception { + if (cancelled) { + throw new IllegalStateException("Received demand after being cancelled"); + } + executor.execute(() -> { + for (int i = 0; i < batchSize && sequence != eofOn; i++) { + ctx.fireChannelRead(sequence++); + } + if (eofOn == sequence) { + ctx.fireChannelInactive(); + } else { + ctx.fireChannelReadComplete(); + } + }); + } + + @Override + public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + if (cancelled) { + throw new IllegalStateException("Cancelled twice"); + } + cancelled = true; + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ClosedLoopChannel.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ClosedLoopChannel.java new file mode 100644 index 000000000000..a687b95e59be --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ClosedLoopChannel.java @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import io.netty.channel.AbstractChannel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelMetadata; +import io.netty.channel.ChannelOutboundBuffer; +import io.netty.channel.ChannelPromise; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.EventLoop; +import java.net.SocketAddress; + +/** + * A closed loop channel that sends no events and receives no events, for testing purposes. + * + * Any outgoing events that reach the channel will throw an exception. All events should be caught + * be inserting a handler that catches them and responds accordingly. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class ClosedLoopChannel extends AbstractChannel { + + private final ChannelConfig config = new DefaultChannelConfig(this); + private static final ChannelMetadata metadata = new ChannelMetadata(false); + + private volatile boolean open = true; + private volatile boolean active = true; + + public ClosedLoopChannel() { + super(null); + } + + public void setOpen(boolean open) { + this.open = open; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + protected AbstractUnsafe newUnsafe() { + return new AbstractUnsafe() { + @Override + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + protected boolean isCompatible(EventLoop loop) { + return true; + } + + @Override + protected SocketAddress localAddress0() { + throw new UnsupportedOperationException(); + } + + @Override + protected SocketAddress remoteAddress0() { + throw new UnsupportedOperationException(); + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected void doDisconnect() throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected void doClose() throws Exception { + this.open = false; + } + + @Override + protected void doBeginRead() throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected void doWrite(ChannelOutboundBuffer in) throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + public ChannelConfig config() { + return config; + } + + @Override + public boolean isOpen() { + return open; + } + + @Override + public boolean isActive() { + return active; + } + + @Override + public ChannelMetadata metadata() { + return metadata; + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/Probe.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/Probe.java new file mode 100644 index 000000000000..172eef460216 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/Probe.java @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import java.util.Date; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class Probe { + + protected final String name; + protected final Long start; + + /** + * Create a new probe and log that it started. + */ + protected Probe(String name) { + this.name = name; + start = System.nanoTime(); + log("Probe created at " + new Date()); + } + + /** + * Create a new probe with the start time from another probe. + */ + protected Probe(String name, long start) { + this.name = name; + this.start = start; + } + + protected void log(String message) { + System.out.println(String.format("%10d %-5s %-15s %s", (System.nanoTime() - start) / 1000, name, Thread.currentThread().getName(), message)); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ProbeHandler.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ProbeHandler.java new file mode 100644 index 000000000000..492aae10bda4 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ProbeHandler.java @@ -0,0 +1,128 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import io.netty.channel.ChannelDuplexHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPromise; +import java.util.LinkedList; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicInteger; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.reactivestreams.tck.SubscriberWhiteboxVerification; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class ProbeHandler extends ChannelDuplexHandler implements SubscriberWhiteboxVerification.SubscriberPuppet { + + private static final int NO_CONTEXT = 0; + private static final int RUN = 1; + private static final int CANCEL = 2; + + private final SubscriberWhiteboxVerification.WhiteboxSubscriberProbe probe; + private final Class clazz; + private final Queue queue = new LinkedList<>(); + private final AtomicInteger state = new AtomicInteger(NO_CONTEXT); + private volatile ChannelHandlerContext ctx; + // Netty doesn't provide a way to send errors out, so we capture whether it was an error or complete here + private volatile Throwable receivedError = null; + + public ProbeHandler(SubscriberWhiteboxVerification.WhiteboxSubscriberProbe probe, Class clazz) { + this.probe = probe; + this.clazz = clazz; + } + + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + this.ctx = ctx; + if (!state.compareAndSet(NO_CONTEXT, RUN)) { + ctx.fireChannelInactive(); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + queue.add(new WriteEvent(clazz.cast(msg), promise)); + } + + @Override + public void close(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { + if (receivedError == null) { + probe.registerOnComplete(); + } else { + probe.registerOnError(receivedError); + } + } + + @Override + public void flush(ChannelHandlerContext ctx) throws Exception { + while (!queue.isEmpty()) { + WriteEvent event = queue.remove(); + probe.registerOnNext(event.msg); + event.future.setSuccess(); + } + } + + @Override + public void triggerRequest(long elements) { + // No need, the channel automatically requests + } + + @Override + public void signalCancel() { + if (!state.compareAndSet(NO_CONTEXT, CANCEL)) { + ctx.fireChannelInactive(); + } + } + + private class WriteEvent { + final T msg; + final ChannelPromise future; + + private WriteEvent(T msg, ChannelPromise future) { + this.msg = msg; + this.future = future; + } + } + + public Subscriber wrap(final Subscriber subscriber) { + return new Subscriber() { + public void onSubscribe(Subscription s) { + probe.registerOnSubscribe(ProbeHandler.this); + subscriber.onSubscribe(s); + } + public void onNext(T t) { + subscriber.onNext(t); + } + public void onError(Throwable t) { + receivedError = t; + subscriber.onError(t); + } + public void onComplete() { + subscriber.onComplete(); + } + }; + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/PublisherProbe.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/PublisherProbe.java new file mode 100644 index 000000000000..22a008eb7bfd --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/PublisherProbe.java @@ -0,0 +1,47 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class PublisherProbe extends Probe implements Publisher { + + private final Publisher publisher; + + public PublisherProbe(Publisher publisher, String name) { + super(name); + this.publisher = publisher; + } + + @Override + public void subscribe(Subscriber s) { + String sName = s == null ? "null" : s.getClass().getName(); + log("invoke subscribe with subscriber " + sName); + publisher.subscribe(new SubscriberProbe<>(s, name, start)); + log("finish subscribe"); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ScheduledBatchedProducer.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ScheduledBatchedProducer.java new file mode 100644 index 000000000000..f814cfce1e80 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/ScheduledBatchedProducer.java @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import io.netty.channel.ChannelHandlerContext; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * A batched producer. + * + * Responds to read requests with batches of elements according to batch size. When eofOn is reached, it closes the + * channel. + * + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class ScheduledBatchedProducer extends BatchedProducer { + + private final ScheduledExecutorService executor; + private final long delay; + + public ScheduledBatchedProducer(long eofOn, int batchSize, long sequence, ScheduledExecutorService executor, long delay) { + super(eofOn, batchSize, sequence, executor); + this.executor = executor; + this.delay = delay; + } + + protected boolean complete; + + @Override + public void read(final ChannelHandlerContext ctx) throws Exception { + executor.schedule(() -> { + for (int i = 0; i < batchSize && sequence != eofOn; i++) { + ctx.fireChannelRead(sequence++); + } + complete = eofOn == sequence; + executor.schedule(() -> { + if (complete) { + ctx.fireChannelInactive(); + } else { + ctx.fireChannelReadComplete(); + } + }, delay, TimeUnit.MILLISECONDS); + }, delay, TimeUnit.MILLISECONDS); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/SubscriberProbe.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/SubscriberProbe.java new file mode 100644 index 000000000000..8003bd9395ba --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/nrs/util/SubscriberProbe.java @@ -0,0 +1,88 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + * + * Original source licensed under the Apache License 2.0 by playframework. + */ + +package software.amazon.awssdk.http.nio.netty.internal.nrs.util; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; + +/** + * This class contains source imported from https://github.com/playframework/netty-reactive-streams, + * licensed under the Apache License 2.0, available at the time of the fork (1/31/2020) here: + * https://github.com/playframework/netty-reactive-streams/blob/master/LICENSE.txt + * + * All original source licensed under the Apache License 2.0 by playframework. All modifications are + * licensed under the Apache License 2.0 by Amazon Web Services. + */ +public class SubscriberProbe extends Probe implements Subscriber { + + private final Subscriber subscriber; + + public SubscriberProbe(Subscriber subscriber, String name) { + super(name); + this.subscriber = subscriber; + } + + SubscriberProbe(Subscriber subscriber, String name, long start) { + super(name, start); + this.subscriber = subscriber; + } + + @Override + public void onSubscribe(final Subscription s) { + String sName = s == null ? "null" : s.getClass().getName(); + log("invoke onSubscribe with subscription " + sName); + subscriber.onSubscribe(new Subscription() { + @Override + public void request(long n) { + log("invoke request " + n); + s.request(n); + log("finish request"); + } + + @Override + public void cancel() { + log("invoke cancel"); + s.cancel(); + log("finish cancel"); + } + }); + log("finish onSubscribe"); + } + + @Override + public void onNext(T t) { + log("invoke onNext with message " + t); + subscriber.onNext(t); + log("finish onNext"); + } + + @Override + public void onError(Throwable t) { + String tName = t == null ? "null" : t.getClass().getName(); + log("invoke onError with " + tName); + subscriber.onError(t); + log("finish onError"); + } + + @Override + public void onComplete() { + log("invoke onComplete"); + subscriber.onComplete(); + log("finish onComplete"); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtilsTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtilsTest.java index 42db5c4a7dcb..1bf5c0e509a5 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtilsTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ package software.amazon.awssdk.http.nio.netty.internal.utils; import static org.assertj.core.api.Assertions.assertThat; -import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_POOL_RECORD; +import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.HTTP2_MULTIPLEXED_CHANNEL_POOL; import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.MAX_CONCURRENT_STREAMS; import io.netty.channel.Channel; @@ -37,7 +37,7 @@ public void testGetAttributes() throws Exception { channel = new MockChannel(); channel.attr(MAX_CONCURRENT_STREAMS).set(1L); assertThat(ChannelUtils.getAttribute(channel, MAX_CONCURRENT_STREAMS).get()).isEqualTo(1L); - assertThat(ChannelUtils.getAttribute(channel, CHANNEL_POOL_RECORD)).isNotPresent(); + assertThat(ChannelUtils.getAttribute(channel, HTTP2_MULTIPLEXED_CHANNEL_POOL)).isNotPresent(); } finally { Optional.ofNullable(channel).ifPresent(Channel::close); } diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtilsTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtilsTest.java index dd9ec85a532b..9b94849de556 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtilsTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ExceptionHandlingUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/OrderedWriteChannelHandlerContextTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/OrderedWriteChannelHandlerContextTest.java new file mode 100644 index 000000000000..cb86d2961e57 --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/OrderedWriteChannelHandlerContextTest.java @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.utils; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Test; + +public class OrderedWriteChannelHandlerContextTest { + @Test + public void wrapOnlyHappensOnce() { + EmbeddedChannel channel = new EmbeddedChannel(new NoOpHandler()); + ChannelHandlerContext ctx = channel.pipeline().context(NoOpHandler.class); + ChannelHandlerContext wrapped = OrderedWriteChannelHandlerContext.wrap(ctx); + ChannelHandlerContext wrapped2 = OrderedWriteChannelHandlerContext.wrap(wrapped); + + assertThat(ctx).isNotSameAs(wrapped); + assertThat(wrapped).isSameAs(wrapped2); + } + + private static class NoOpHandler implements ChannelHandler { + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + + } + + @Override + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + + } + } + +} \ No newline at end of file diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java index 24827ef211e8..2dbfefa4e46d 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/resources/jetty-logging.properties b/http-clients/netty-nio-client/src/test/resources/jetty-logging.properties index 9cfe41c4144e..4ee410e7fa92 100644 --- a/http-clients/netty-nio-client/src/test/resources/jetty-logging.properties +++ b/http-clients/netty-nio-client/src/test/resources/jetty-logging.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/http-clients/netty-nio-client/src/test/resources/log4j.properties b/http-clients/netty-nio-client/src/test/resources/log4j.properties index 0780928c7264..6fa311bc45f9 100644 --- a/http-clients/netty-nio-client/src/test/resources/log4j.properties +++ b/http-clients/netty-nio-client/src/test/resources/log4j.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. @@ -13,7 +13,7 @@ # permissions and limitations under the License. # -log4j.rootLogger=ERROR, A1 +log4j.rootLogger=INFO, A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout diff --git a/http-clients/pom.xml b/http-clients/pom.xml index b8fa161e5837..78083e0101ea 100644 --- a/http-clients/pom.xml +++ b/http-clients/pom.xml @@ -1,7 +1,7 @@ - 4.1.42.Final + 4.1.46.Final 3.3 1.3 UTF-8 3.1.11 - 2.0.3 + 2.0.4 2.3.24-incubating 1.11.1 3.10.0 @@ -117,24 +116,25 @@ 1.10.19 2.28.2 3.8.0 - 26.0-jre + 28.2-jre 1.1 + 7.1.0 2.3 - 2.0.25.Final + 2.0.29.Final 1.11.477 1.0.392 - 2.21.0 + 2.22.2 3.8.1 - 2.17 - 2.22.0 + 3.1.1 + 2.22.2 3.1.1 3.0.1 3.1.1 1.6 - 8.18 - 0.8.2 + 8.29 + 0.8.5 1.6.8 1.6.0 2.8.2 @@ -405,6 +405,11 @@ com.fasterxml.jackson.core:* org.slf4j:slf4j-api + + + com.typesafe.netty:* + software.amazon.awssdk:aws-sdk-java + @@ -418,9 +423,6 @@ org.jacoco jacoco-maven-plugin ${jacoco-maven-plugin.version} - - ${sonar.jacoco.reportPath} - default-prepare-agent @@ -473,27 +475,6 @@ true true - - - - org.jacoco - jacoco-maven-plugin - ${jacoco-maven-plugin.version} - - ${sonar.jacoco.reportPath} - true - - - - default-prepare-agent - - prepare-agent - - - - - - diff --git a/release-scripts/pom.xml b/release-scripts/pom.xml index f5bb1d026dd8..9ca2b59bd6c7 100644 --- a/release-scripts/pom.xml +++ b/release-scripts/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + accessanalyzer + AWS Java SDK :: Services :: AccessAnalyzer + The AWS Java SDK for AccessAnalyzer module holds the client classes that are used for + communicating with AccessAnalyzer. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.accessanalyzer + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/accessanalyzer/src/main/resources/codegen-resources/paginators-1.json b/services/accessanalyzer/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..64553f7bdf1c --- /dev/null +++ b/services/accessanalyzer/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListAnalyzedResources": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analyzedResources" + }, + "ListAnalyzers": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "analyzers" + }, + "ListArchiveRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "archiveRules" + }, + "ListFindings": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "findings" + } + } +} diff --git a/services/accessanalyzer/src/main/resources/codegen-resources/service-2.json b/services/accessanalyzer/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..cd8928f5cf38 --- /dev/null +++ b/services/accessanalyzer/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,1589 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-11-01", + "endpointPrefix":"access-analyzer", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceFullName":"Access Analyzer", + "serviceId":"AccessAnalyzer", + "signatureVersion":"v4", + "signingName":"access-analyzer", + "uid":"accessanalyzer-2019-11-01" + }, + "operations":{ + "CreateAnalyzer":{ + "name":"CreateAnalyzer", + "http":{ + "method":"PUT", + "requestUri":"/analyzer", + "responseCode":200 + }, + "input":{"shape":"CreateAnalyzerRequest"}, + "output":{"shape":"CreateAnalyzerResponse"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Creates an analyzer for your account.

    ", + "idempotent":true + }, + "CreateArchiveRule":{ + "name":"CreateArchiveRule", + "http":{ + "method":"PUT", + "requestUri":"/analyzer/{analyzerName}/archive-rule", + "responseCode":200 + }, + "input":{"shape":"CreateArchiveRuleRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Creates an archive rule for the specified analyzer. Archive rules automatically archive findings that meet the criteria you define when you create the rule.

    ", + "idempotent":true + }, + "DeleteAnalyzer":{ + "name":"DeleteAnalyzer", + "http":{ + "method":"DELETE", + "requestUri":"/analyzer/{analyzerName}", + "responseCode":200 + }, + "input":{"shape":"DeleteAnalyzerRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Deletes the specified analyzer. When you delete an analyzer, Access Analyzer is disabled for the account in the current or specific Region. All findings that were generated by the analyzer are deleted. You cannot undo this action.

    ", + "idempotent":true + }, + "DeleteArchiveRule":{ + "name":"DeleteArchiveRule", + "http":{ + "method":"DELETE", + "requestUri":"/analyzer/{analyzerName}/archive-rule/{ruleName}", + "responseCode":200 + }, + "input":{"shape":"DeleteArchiveRuleRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Deletes the specified archive rule.

    ", + "idempotent":true + }, + "GetAnalyzedResource":{ + "name":"GetAnalyzedResource", + "http":{ + "method":"GET", + "requestUri":"/analyzed-resource", + "responseCode":200 + }, + "input":{"shape":"GetAnalyzedResourceRequest"}, + "output":{"shape":"GetAnalyzedResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves information about a resource that was analyzed.

    " + }, + "GetAnalyzer":{ + "name":"GetAnalyzer", + "http":{ + "method":"GET", + "requestUri":"/analyzer/{analyzerName}", + "responseCode":200 + }, + "input":{"shape":"GetAnalyzerRequest"}, + "output":{"shape":"GetAnalyzerResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves information about the specified analyzer.

    " + }, + "GetArchiveRule":{ + "name":"GetArchiveRule", + "http":{ + "method":"GET", + "requestUri":"/analyzer/{analyzerName}/archive-rule/{ruleName}", + "responseCode":200 + }, + "input":{"shape":"GetArchiveRuleRequest"}, + "output":{"shape":"GetArchiveRuleResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves information about an archive rule.

    " + }, + "GetFinding":{ + "name":"GetFinding", + "http":{ + "method":"GET", + "requestUri":"/finding/{id}", + "responseCode":200 + }, + "input":{"shape":"GetFindingRequest"}, + "output":{"shape":"GetFindingResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves information about the specified finding.

    " + }, + "ListAnalyzedResources":{ + "name":"ListAnalyzedResources", + "http":{ + "method":"POST", + "requestUri":"/analyzed-resource", + "responseCode":200 + }, + "input":{"shape":"ListAnalyzedResourcesRequest"}, + "output":{"shape":"ListAnalyzedResourcesResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves a list of resources of the specified type that have been analyzed by the specified analyzer..

    " + }, + "ListAnalyzers":{ + "name":"ListAnalyzers", + "http":{ + "method":"GET", + "requestUri":"/analyzer", + "responseCode":200 + }, + "input":{"shape":"ListAnalyzersRequest"}, + "output":{"shape":"ListAnalyzersResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves a list of analyzers.

    " + }, + "ListArchiveRules":{ + "name":"ListArchiveRules", + "http":{ + "method":"GET", + "requestUri":"/analyzer/{analyzerName}/archive-rule", + "responseCode":200 + }, + "input":{"shape":"ListArchiveRulesRequest"}, + "output":{"shape":"ListArchiveRulesResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves a list of archive rules created for the specified analyzer.

    " + }, + "ListFindings":{ + "name":"ListFindings", + "http":{ + "method":"POST", + "requestUri":"/finding", + "responseCode":200 + }, + "input":{"shape":"ListFindingsRequest"}, + "output":{"shape":"ListFindingsResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves a list of findings generated by the specified analyzer.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"GET", + "requestUri":"/tags/{resourceArn}", + "responseCode":200 + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Retrieves a list of tags applied to the specified resource.

    " + }, + "StartResourceScan":{ + "name":"StartResourceScan", + "http":{ + "method":"POST", + "requestUri":"/resource/scan", + "responseCode":200 + }, + "input":{"shape":"StartResourceScanRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Immediately starts a scan of the policies applied to the specified resource.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/tags/{resourceArn}", + "responseCode":200 + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Adds a tag to the specified resource.

    ", + "idempotent":true + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"DELETE", + "requestUri":"/tags/{resourceArn}", + "responseCode":200 + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Removes a tag from the specified resource.

    ", + "idempotent":true + }, + "UpdateArchiveRule":{ + "name":"UpdateArchiveRule", + "http":{ + "method":"PUT", + "requestUri":"/analyzer/{analyzerName}/archive-rule/{ruleName}", + "responseCode":200 + }, + "input":{"shape":"UpdateArchiveRuleRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Updates the criteria and values for the specified archive rule.

    ", + "idempotent":true + }, + "UpdateFindings":{ + "name":"UpdateFindings", + "http":{ + "method":"PUT", + "requestUri":"/finding", + "responseCode":200 + }, + "input":{"shape":"UpdateFindingsRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Updates the status for the specified findings.

    ", + "idempotent":true + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "error":{ + "httpStatusCode":403, + "senderFault":true + }, + "exception":true + }, + "ActionList":{ + "type":"list", + "member":{"shape":"String"} + }, + "AnalyzedResource":{ + "type":"structure", + "required":[ + "analyzedAt", + "createdAt", + "isPublic", + "resourceArn", + "resourceOwnerAccount", + "resourceType", + "updatedAt" + ], + "members":{ + "actions":{ + "shape":"ActionList", + "documentation":"

    The actions that an external principal is granted permission to use by the policy that generated the finding.

    " + }, + "analyzedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the resource was analyzed.

    " + }, + "createdAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the finding was created.

    " + }, + "error":{ + "shape":"String", + "documentation":"

    An error message.

    " + }, + "isPublic":{ + "shape":"Boolean", + "documentation":"

    Indicates whether the policy that generated the finding grants public access to the resource.

    " + }, + "resourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The ARN of the resource that was analyzed.

    " + }, + "resourceOwnerAccount":{ + "shape":"String", + "documentation":"

    The AWS account ID that owns the resource.

    " + }, + "resourceType":{ + "shape":"ResourceType", + "documentation":"

    The type of the resource that was analyzed.

    " + }, + "sharedVia":{ + "shape":"SharedViaList", + "documentation":"

    Indicates how the access that generated the finding is granted.

    " + }, + "status":{ + "shape":"FindingStatus", + "documentation":"

    The current status of the finding generated from the analyzed resource.

    " + }, + "updatedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the finding was updated.

    " + } + }, + "documentation":"

    Contains details about the analyzed resource.

    " + }, + "AnalyzedResourceSummary":{ + "type":"structure", + "required":[ + "resourceArn", + "resourceOwnerAccount", + "resourceType" + ], + "members":{ + "resourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The ARN of the analyzed resource.

    " + }, + "resourceOwnerAccount":{ + "shape":"String", + "documentation":"

    The AWS account ID that owns the resource.

    " + }, + "resourceType":{ + "shape":"ResourceType", + "documentation":"

    The type of resource that was analyzed.

    " + } + }, + "documentation":"

    Contains the ARN of the analyzed resource.

    " + }, + "AnalyzedResourcesList":{ + "type":"list", + "member":{"shape":"AnalyzedResourceSummary"} + }, + "AnalyzerArn":{ + "type":"string", + "pattern":"^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:analyzer/.{1,255}$" + }, + "AnalyzerStatus":{ + "type":"string", + "enum":[ + "ACTIVE", + "CREATING", + "DISABLED", + "FAILED" + ] + }, + "AnalyzerSummary":{ + "type":"structure", + "required":[ + "arn", + "createdAt", + "name", + "status", + "type" + ], + "members":{ + "arn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer.

    " + }, + "createdAt":{ + "shape":"Timestamp", + "documentation":"

    A timestamp for the time at which the analyzer was created.

    " + }, + "lastResourceAnalyzed":{ + "shape":"String", + "documentation":"

    The resource that was most recently analyzed by the analyzer.

    " + }, + "lastResourceAnalyzedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the most recently analyzed resource was analyzed.

    " + }, + "name":{ + "shape":"Name", + "documentation":"

    The name of the analyzer.

    " + }, + "status":{ + "shape":"AnalyzerStatus", + "documentation":"

    The status of the analyzer. An Active analyzer successfully monitors supported resources and generates new findings. The analyzer is Disabled when a user action, such as removing trusted access for IAM Access Analyzer from AWS Organizations, causes the analyzer to stop generating new findings. The status is Creating when the analyzer creation is in progress and Failed when the analyzer creation has failed.

    " + }, + "statusReason":{ + "shape":"StatusReason", + "documentation":"

    The statusReason provides more details about the current status of the analyzer. For example, if the creation for the analyzer fails, a Failed status is displayed. For an analyzer with organization as the type, this failure can be due to an issue with creating the service-linked roles required in the member accounts of the AWS organization.

    " + }, + "tags":{ + "shape":"TagsMap", + "documentation":"

    The tags added to the analyzer.

    " + }, + "type":{ + "shape":"Type", + "documentation":"

    The type of analyzer, which corresponds to the zone of trust chosen for the analyzer.

    " + } + }, + "documentation":"

    Contains information about the analyzer.

    " + }, + "AnalyzersList":{ + "type":"list", + "member":{"shape":"AnalyzerSummary"} + }, + "ArchiveRuleSummary":{ + "type":"structure", + "required":[ + "createdAt", + "filter", + "ruleName", + "updatedAt" + ], + "members":{ + "createdAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the archive rule was created.

    " + }, + "filter":{ + "shape":"FilterCriteriaMap", + "documentation":"

    A filter used to define the archive rule.

    " + }, + "ruleName":{ + "shape":"Name", + "documentation":"

    The name of the archive rule.

    " + }, + "updatedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the archive rule was last updated.

    " + } + }, + "documentation":"

    Contains information about an archive rule.

    " + }, + "ArchiveRulesList":{ + "type":"list", + "member":{"shape":"ArchiveRuleSummary"} + }, + "Boolean":{ + "type":"boolean", + "box":true + }, + "ConditionKeyMap":{ + "type":"map", + "key":{"shape":"String"}, + "value":{"shape":"String"} + }, + "ConflictException":{ + "type":"structure", + "required":[ + "message", + "resourceId", + "resourceType" + ], + "members":{ + "message":{"shape":"String"}, + "resourceId":{ + "shape":"String", + "documentation":"

    The ID of the resource.

    " + }, + "resourceType":{ + "shape":"String", + "documentation":"

    The resource type.

    " + } + }, + "documentation":"

    A conflict exception error.

    ", + "error":{ + "httpStatusCode":409, + "senderFault":true + }, + "exception":true + }, + "CreateAnalyzerRequest":{ + "type":"structure", + "required":[ + "analyzerName", + "type" + ], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer to create.

    " + }, + "archiveRules":{ + "shape":"InlineArchiveRulesList", + "documentation":"

    Specifies the archive rules to add for the analyzer. Archive rules automatically archive findings that meet the criteria you define for the rule.

    " + }, + "clientToken":{ + "shape":"String", + "documentation":"

    A client token.

    ", + "idempotencyToken":true + }, + "tags":{ + "shape":"TagsMap", + "documentation":"

    The tags to apply to the analyzer.

    " + }, + "type":{ + "shape":"Type", + "documentation":"

    The type of analyzer to create. Only ACCOUNT analyzers are supported. You can create only one analyzer per account per Region.

    " + } + }, + "documentation":"

    Creates an analyzer.

    " + }, + "CreateAnalyzerResponse":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer that was created by the request.

    " + } + }, + "documentation":"

    The response to the request to create an analyzer.

    " + }, + "CreateArchiveRuleRequest":{ + "type":"structure", + "required":[ + "analyzerName", + "filter", + "ruleName" + ], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the created analyzer.

    ", + "location":"uri", + "locationName":"analyzerName" + }, + "clientToken":{ + "shape":"String", + "documentation":"

    A client token.

    ", + "idempotencyToken":true + }, + "filter":{ + "shape":"FilterCriteriaMap", + "documentation":"

    The criteria for the rule.

    " + }, + "ruleName":{ + "shape":"Name", + "documentation":"

    The name of the rule to create.

    " + } + }, + "documentation":"

    Creates an archive rule.

    " + }, + "Criterion":{ + "type":"structure", + "members":{ + "contains":{ + "shape":"ValueList", + "documentation":"

    A \"contains\" operator to match for the filter used to create the rule.

    " + }, + "eq":{ + "shape":"ValueList", + "documentation":"

    An \"equals\" operator to match for the filter used to create the rule.

    " + }, + "exists":{ + "shape":"Boolean", + "documentation":"

    An \"exists\" operator to match for the filter used to create the rule.

    " + }, + "neq":{ + "shape":"ValueList", + "documentation":"

    A \"not equals\" operator to match for the filter used to create the rule.

    " + } + }, + "documentation":"

    The criteria to use in the filter that defines the archive rule.

    " + }, + "DeleteAnalyzerRequest":{ + "type":"structure", + "required":["analyzerName"], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer to delete.

    ", + "location":"uri", + "locationName":"analyzerName" + }, + "clientToken":{ + "shape":"String", + "documentation":"

    A client token.

    ", + "idempotencyToken":true, + "location":"querystring", + "locationName":"clientToken" + } + }, + "documentation":"

    Deletes an analyzer.

    " + }, + "DeleteArchiveRuleRequest":{ + "type":"structure", + "required":[ + "analyzerName", + "ruleName" + ], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer that associated with the archive rule to delete.

    ", + "location":"uri", + "locationName":"analyzerName" + }, + "clientToken":{ + "shape":"String", + "documentation":"

    A client token.

    ", + "idempotencyToken":true, + "location":"querystring", + "locationName":"clientToken" + }, + "ruleName":{ + "shape":"Name", + "documentation":"

    The name of the rule to delete.

    ", + "location":"uri", + "locationName":"ruleName" + } + }, + "documentation":"

    Deletes an archive rule.

    " + }, + "FilterCriteriaMap":{ + "type":"map", + "key":{"shape":"String"}, + "value":{"shape":"Criterion"} + }, + "Finding":{ + "type":"structure", + "required":[ + "analyzedAt", + "condition", + "createdAt", + "id", + "resourceOwnerAccount", + "resourceType", + "status", + "updatedAt" + ], + "members":{ + "action":{ + "shape":"ActionList", + "documentation":"

    The action in the analyzed policy statement that an external principal has permission to use.

    " + }, + "analyzedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the resource was analyzed.

    " + }, + "condition":{ + "shape":"ConditionKeyMap", + "documentation":"

    The condition in the analyzed policy statement that resulted in a finding.

    " + }, + "createdAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the finding was generated.

    " + }, + "error":{ + "shape":"String", + "documentation":"

    An error.

    " + }, + "id":{ + "shape":"FindingId", + "documentation":"

    The ID of the finding.

    " + }, + "isPublic":{ + "shape":"Boolean", + "documentation":"

    Indicates whether the policy that generated the finding allows public access to the resource.

    " + }, + "principal":{ + "shape":"PrincipalMap", + "documentation":"

    The external principal that access to a resource within the zone of trust.

    " + }, + "resource":{ + "shape":"String", + "documentation":"

    The resource that an external principal has access to.

    " + }, + "resourceOwnerAccount":{ + "shape":"String", + "documentation":"

    The AWS account ID that owns the resource.

    " + }, + "resourceType":{ + "shape":"ResourceType", + "documentation":"

    The type of the resource reported in the finding.

    " + }, + "status":{ + "shape":"FindingStatus", + "documentation":"

    The current status of the finding.

    " + }, + "updatedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the finding was updated.

    " + } + }, + "documentation":"

    Contains information about a finding.

    " + }, + "FindingId":{"type":"string"}, + "FindingIdList":{ + "type":"list", + "member":{"shape":"FindingId"} + }, + "FindingStatus":{ + "type":"string", + "enum":[ + "ACTIVE", + "ARCHIVED", + "RESOLVED" + ] + }, + "FindingStatusUpdate":{ + "type":"string", + "enum":[ + "ACTIVE", + "ARCHIVED" + ] + }, + "FindingSummary":{ + "type":"structure", + "required":[ + "analyzedAt", + "condition", + "createdAt", + "id", + "resourceOwnerAccount", + "resourceType", + "status", + "updatedAt" + ], + "members":{ + "action":{ + "shape":"ActionList", + "documentation":"

    The action in the analyzed policy statement that an external principal has permission to use.

    " + }, + "analyzedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the resource-based policy that generated the finding was analyzed.

    " + }, + "condition":{ + "shape":"ConditionKeyMap", + "documentation":"

    The condition in the analyzed policy statement that resulted in a finding.

    " + }, + "createdAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the finding was created.

    " + }, + "error":{ + "shape":"String", + "documentation":"

    The error that resulted in an Error finding.

    " + }, + "id":{ + "shape":"FindingId", + "documentation":"

    The ID of the finding.

    " + }, + "isPublic":{ + "shape":"Boolean", + "documentation":"

    Indicates whether the finding reports a resource that has a policy that allows public access.

    " + }, + "principal":{ + "shape":"PrincipalMap", + "documentation":"

    The external principal that has access to a resource within the zone of trust.

    " + }, + "resource":{ + "shape":"String", + "documentation":"

    The resource that the external principal has access to.

    " + }, + "resourceOwnerAccount":{ + "shape":"String", + "documentation":"

    The AWS account ID that owns the resource.

    " + }, + "resourceType":{ + "shape":"ResourceType", + "documentation":"

    The type of the resource that the external principal has access to.

    " + }, + "status":{ + "shape":"FindingStatus", + "documentation":"

    The status of the finding.

    " + }, + "updatedAt":{ + "shape":"Timestamp", + "documentation":"

    The time at which the finding was most recently updated.

    " + } + }, + "documentation":"

    Contains information about a finding.

    " + }, + "FindingsList":{ + "type":"list", + "member":{"shape":"FindingSummary"} + }, + "GetAnalyzedResourceRequest":{ + "type":"structure", + "required":[ + "analyzerArn", + "resourceArn" + ], + "members":{ + "analyzerArn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer to retrieve information from.

    ", + "location":"querystring", + "locationName":"analyzerArn" + }, + "resourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The ARN of the resource to retrieve information about.

    ", + "location":"querystring", + "locationName":"resourceArn" + } + }, + "documentation":"

    Retrieves an analyzed resource.

    " + }, + "GetAnalyzedResourceResponse":{ + "type":"structure", + "members":{ + "resource":{ + "shape":"AnalyzedResource", + "documentation":"

    An AnalyedResource object that contains information that Access Analyzer found when it analyzed the resource.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "GetAnalyzerRequest":{ + "type":"structure", + "required":["analyzerName"], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer retrieved.

    ", + "location":"uri", + "locationName":"analyzerName" + } + }, + "documentation":"

    Retrieves an analyzer.

    " + }, + "GetAnalyzerResponse":{ + "type":"structure", + "required":["analyzer"], + "members":{ + "analyzer":{ + "shape":"AnalyzerSummary", + "documentation":"

    An AnalyzerSummary object that contains information about the analyzer.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "GetArchiveRuleRequest":{ + "type":"structure", + "required":[ + "analyzerName", + "ruleName" + ], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer to retrieve rules from.

    ", + "location":"uri", + "locationName":"analyzerName" + }, + "ruleName":{ + "shape":"Name", + "documentation":"

    The name of the rule to retrieve.

    ", + "location":"uri", + "locationName":"ruleName" + } + }, + "documentation":"

    Retrieves an archive rule.

    " + }, + "GetArchiveRuleResponse":{ + "type":"structure", + "required":["archiveRule"], + "members":{ + "archiveRule":{"shape":"ArchiveRuleSummary"} + }, + "documentation":"

    The response to the request.

    " + }, + "GetFindingRequest":{ + "type":"structure", + "required":[ + "analyzerArn", + "id" + ], + "members":{ + "analyzerArn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer that generated the finding.

    ", + "location":"querystring", + "locationName":"analyzerArn" + }, + "id":{ + "shape":"FindingId", + "documentation":"

    The ID of the finding to retrieve.

    ", + "location":"uri", + "locationName":"id" + } + }, + "documentation":"

    Retrieves a finding.

    " + }, + "GetFindingResponse":{ + "type":"structure", + "members":{ + "finding":{ + "shape":"Finding", + "documentation":"

    A finding object that contains finding details.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "InlineArchiveRule":{ + "type":"structure", + "required":[ + "filter", + "ruleName" + ], + "members":{ + "filter":{ + "shape":"FilterCriteriaMap", + "documentation":"

    The condition and values for a criterion.

    " + }, + "ruleName":{ + "shape":"Name", + "documentation":"

    The name of the rule.

    " + } + }, + "documentation":"

    An criterion statement in an archive rule. Each archive rule may have multiple criteria.

    " + }, + "InlineArchiveRulesList":{ + "type":"list", + "member":{"shape":"InlineArchiveRule"} + }, + "Integer":{ + "type":"integer", + "box":true + }, + "InternalServerException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"}, + "retryAfterSeconds":{ + "shape":"Integer", + "documentation":"

    The seconds to wait to retry.

    ", + "location":"header", + "locationName":"Retry-After" + } + }, + "documentation":"

    Internal server error.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "ListAnalyzedResourcesRequest":{ + "type":"structure", + "required":["analyzerArn"], + "members":{ + "analyzerArn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer to retrieve a list of analyzed resources from.

    " + }, + "maxResults":{ + "shape":"Integer", + "documentation":"

    The maximum number of results to return in the response.

    " + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    " + }, + "resourceType":{ + "shape":"ResourceType", + "documentation":"

    The type of resource.

    " + } + }, + "documentation":"

    Retrieves a list of resources that have been analyzed.

    " + }, + "ListAnalyzedResourcesResponse":{ + "type":"structure", + "required":["analyzedResources"], + "members":{ + "analyzedResources":{ + "shape":"AnalyzedResourcesList", + "documentation":"

    A list of resources that were analyzed.

    " + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "ListAnalyzersRequest":{ + "type":"structure", + "members":{ + "maxResults":{ + "shape":"Integer", + "documentation":"

    The maximum number of results to return in the response.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    ", + "location":"querystring", + "locationName":"nextToken" + }, + "type":{ + "shape":"Type", + "documentation":"

    The type of analyzer.

    ", + "location":"querystring", + "locationName":"type" + } + }, + "documentation":"

    Retrieves a list of analyzers.

    " + }, + "ListAnalyzersResponse":{ + "type":"structure", + "required":["analyzers"], + "members":{ + "analyzers":{ + "shape":"AnalyzersList", + "documentation":"

    The analyzers retrieved.

    " + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "ListArchiveRulesRequest":{ + "type":"structure", + "required":["analyzerName"], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer to retrieve rules from.

    ", + "location":"uri", + "locationName":"analyzerName" + }, + "maxResults":{ + "shape":"Integer", + "documentation":"

    The maximum number of results to return in the request.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    ", + "location":"querystring", + "locationName":"nextToken" + } + }, + "documentation":"

    Retrieves a list of archive rules created for the specified analyzer.

    " + }, + "ListArchiveRulesResponse":{ + "type":"structure", + "required":["archiveRules"], + "members":{ + "archiveRules":{ + "shape":"ArchiveRulesList", + "documentation":"

    A list of archive rules created for the specified analyzer.

    " + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "ListFindingsRequest":{ + "type":"structure", + "required":["analyzerArn"], + "members":{ + "analyzerArn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer to retrieve findings from.

    " + }, + "filter":{ + "shape":"FilterCriteriaMap", + "documentation":"

    A filter to match for the findings to return.

    " + }, + "maxResults":{ + "shape":"Integer", + "documentation":"

    The maximum number of results to return in the response.

    " + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    " + }, + "sort":{ + "shape":"SortCriteria", + "documentation":"

    The sort order for the findings returned.

    " + } + }, + "documentation":"

    Retrieves a list of findings generated by the specified analyzer.

    " + }, + "ListFindingsResponse":{ + "type":"structure", + "required":["findings"], + "members":{ + "findings":{ + "shape":"FindingsList", + "documentation":"

    A list of findings retrieved from the analyzer that match the filter criteria specified, if any.

    " + }, + "nextToken":{ + "shape":"Token", + "documentation":"

    A token used for pagination of results returned.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["resourceArn"], + "members":{ + "resourceArn":{ + "shape":"String", + "documentation":"

    The ARN of the resource to retrieve tags from.

    ", + "location":"uri", + "locationName":"resourceArn" + } + }, + "documentation":"

    Retrieves a list of tags applied to the specified resource.

    " + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "members":{ + "tags":{ + "shape":"TagsMap", + "documentation":"

    The tags that are applied to the specified resource.

    " + } + }, + "documentation":"

    The response to the request.

    " + }, + "Name":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[A-Za-z][A-Za-z0-9_.-]*$" + }, + "OrderBy":{ + "type":"string", + "enum":[ + "ASC", + "DESC" + ] + }, + "PrincipalMap":{ + "type":"map", + "key":{"shape":"String"}, + "value":{"shape":"String"} + }, + "ReasonCode":{ + "type":"string", + "enum":[ + "AWS_SERVICE_ACCESS_DISABLED", + "DELEGATED_ADMINISTRATOR_DEREGISTERED", + "ORGANIZATION_DELETED", + "SERVICE_LINKED_ROLE_CREATION_FAILED" + ] + }, + "ResourceArn":{ + "type":"string", + "pattern":"arn:[^:]*:[^:]*:[^:]*:[^:]*:.*$" + }, + "ResourceNotFoundException":{ + "type":"structure", + "required":[ + "message", + "resourceId", + "resourceType" + ], + "members":{ + "message":{"shape":"String"}, + "resourceId":{ + "shape":"String", + "documentation":"

    The ID of the resource.

    " + }, + "resourceType":{ + "shape":"String", + "documentation":"

    The type of the resource.

    " + } + }, + "documentation":"

    The specified resource could not be found.

    ", + "error":{ + "httpStatusCode":404, + "senderFault":true + }, + "exception":true + }, + "ResourceType":{ + "type":"string", + "enum":[ + "AWS::IAM::Role", + "AWS::KMS::Key", + "AWS::Lambda::Function", + "AWS::Lambda::LayerVersion", + "AWS::S3::Bucket", + "AWS::SQS::Queue" + ] + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "required":[ + "message", + "resourceId", + "resourceType" + ], + "members":{ + "message":{"shape":"String"}, + "resourceId":{ + "shape":"String", + "documentation":"

    The resource ID.

    " + }, + "resourceType":{ + "shape":"String", + "documentation":"

    The resource type.

    " + } + }, + "documentation":"

    Service quote met error.

    ", + "error":{ + "httpStatusCode":402, + "senderFault":true + }, + "exception":true + }, + "SharedViaList":{ + "type":"list", + "member":{"shape":"String"} + }, + "SortCriteria":{ + "type":"structure", + "members":{ + "attributeName":{ + "shape":"String", + "documentation":"

    The name of the attribute to sort on.

    " + }, + "orderBy":{ + "shape":"OrderBy", + "documentation":"

    The sort order, ascending or descending.

    " + } + }, + "documentation":"

    The criteria used to sort.

    " + }, + "StartResourceScanRequest":{ + "type":"structure", + "required":[ + "analyzerArn", + "resourceArn" + ], + "members":{ + "analyzerArn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer to use to scan the policies applied to the specified resource.

    " + }, + "resourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The ARN of the resource to scan.

    " + } + }, + "documentation":"

    Starts a scan of the policies applied to the specified resource.

    " + }, + "StatusReason":{ + "type":"structure", + "required":["code"], + "members":{ + "code":{ + "shape":"ReasonCode", + "documentation":"

    The reason code for the current status of the analyzer.

    " + } + }, + "documentation":"

    Provides more details about the current status of the analyzer. For example, if the creation for the analyzer fails, a Failed status is displayed. For an analyzer with organization as the type, this failure can be due to an issue with creating the service-linked roles required in the member accounts of the AWS organization.

    " + }, + "String":{"type":"string"}, + "TagKeys":{ + "type":"list", + "member":{"shape":"String"} + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tags" + ], + "members":{ + "resourceArn":{ + "shape":"String", + "documentation":"

    The ARN of the resource to add the tag to.

    ", + "location":"uri", + "locationName":"resourceArn" + }, + "tags":{ + "shape":"TagsMap", + "documentation":"

    The tags to add to the resource.

    " + } + }, + "documentation":"

    Adds a tag to the specified resource.

    " + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The response to the request.

    " + }, + "TagsMap":{ + "type":"map", + "key":{"shape":"String"}, + "value":{"shape":"String"} + }, + "ThrottlingException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"}, + "retryAfterSeconds":{ + "shape":"Integer", + "documentation":"

    The seconds to wait to retry.

    ", + "location":"header", + "locationName":"Retry-After" + } + }, + "documentation":"

    Throttling limit exceeded error.

    ", + "error":{ + "httpStatusCode":429, + "senderFault":true + }, + "exception":true + }, + "Timestamp":{ + "type":"timestamp", + "timestampFormat":"iso8601" + }, + "Token":{"type":"string"}, + "Type":{ + "type":"string", + "enum":[ + "ACCOUNT", + "ORGANIZATION" + ] + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tagKeys" + ], + "members":{ + "resourceArn":{ + "shape":"String", + "documentation":"

    The ARN of the resource to remove the tag from.

    ", + "location":"uri", + "locationName":"resourceArn" + }, + "tagKeys":{ + "shape":"TagKeys", + "documentation":"

    The key for the tag to add.

    ", + "location":"querystring", + "locationName":"tagKeys" + } + }, + "documentation":"

    Removes a tag from the specified resource.

    " + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The response to the request.

    " + }, + "UpdateArchiveRuleRequest":{ + "type":"structure", + "required":[ + "analyzerName", + "filter", + "ruleName" + ], + "members":{ + "analyzerName":{ + "shape":"Name", + "documentation":"

    The name of the analyzer to update the archive rules for.

    ", + "location":"uri", + "locationName":"analyzerName" + }, + "clientToken":{ + "shape":"String", + "documentation":"

    A client token.

    ", + "idempotencyToken":true + }, + "filter":{ + "shape":"FilterCriteriaMap", + "documentation":"

    A filter to match for the rules to update. Only rules that match the filter are updated.

    " + }, + "ruleName":{ + "shape":"Name", + "documentation":"

    The name of the rule to update.

    ", + "location":"uri", + "locationName":"ruleName" + } + }, + "documentation":"

    Updates the specified archive rule.

    " + }, + "UpdateFindingsRequest":{ + "type":"structure", + "required":[ + "analyzerArn", + "status" + ], + "members":{ + "analyzerArn":{ + "shape":"AnalyzerArn", + "documentation":"

    The ARN of the analyzer that generated the findings to update.

    " + }, + "clientToken":{ + "shape":"String", + "documentation":"

    A client token.

    ", + "idempotencyToken":true + }, + "ids":{ + "shape":"FindingIdList", + "documentation":"

    The IDs of the findings to update.

    " + }, + "resourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The ARN of the resource identified in the finding.

    " + }, + "status":{ + "shape":"FindingStatusUpdate", + "documentation":"

    The state represents the action to take to update the finding Status. Use ARCHIVE to change an Active finding to an Archived finding. Use ACTIVE to change an Archived finding to an Active finding.

    " + } + }, + "documentation":"

    Updates findings with the new values provided in the request.

    " + }, + "ValidationException":{ + "type":"structure", + "required":[ + "message", + "reason" + ], + "members":{ + "fieldList":{ + "shape":"ValidationExceptionFieldList", + "documentation":"

    A list of fields that didn't validate.

    " + }, + "message":{"shape":"String"}, + "reason":{ + "shape":"ValidationExceptionReason", + "documentation":"

    The reason for the exception.

    " + } + }, + "documentation":"

    Validation exception error.

    ", + "error":{ + "httpStatusCode":400, + "senderFault":true + }, + "exception":true + }, + "ValidationExceptionField":{ + "type":"structure", + "required":[ + "message", + "name" + ], + "members":{ + "message":{ + "shape":"String", + "documentation":"

    A message about the validation exception.

    " + }, + "name":{ + "shape":"String", + "documentation":"

    The name of the validation exception.

    " + } + }, + "documentation":"

    Contains information about a validation exception.

    " + }, + "ValidationExceptionFieldList":{ + "type":"list", + "member":{"shape":"ValidationExceptionField"} + }, + "ValidationExceptionReason":{ + "type":"string", + "enum":[ + "cannotParse", + "fieldValidationFailed", + "other", + "unknownOperation" + ] + }, + "ValueList":{ + "type":"list", + "member":{"shape":"String"}, + "max":20, + "min":1 + } + }, + "documentation":"

    AWS IAM Access Analyzer helps identify potential resource-access risks by enabling you to identify any policies that grant access to an external principal. It does this by using logic-based reasoning to analyze resource-based policies in your AWS environment. An external principal can be another AWS account, a root user, an IAM user or role, a federated user, an AWS service, or an anonymous user. This guide describes the AWS IAM Access Analyzer operations that you can call programmatically. For general information about Access Analyzer, see the AWS IAM Access Analyzer section of the IAM User Guide.

    To start using Access Analyzer, you first need to create an analyzer.

    " +} diff --git a/services/acm/build.properties b/services/acm/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/acm/build.properties +++ b/services/acm/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/acm/pom.xml b/services/acm/pom.xml index bff0104e62c5..3430494dfa61 100644 --- a/services/acm/pom.xml +++ b/services/acm/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + appconfig + AWS Java SDK :: Services :: AppConfig + The AWS Java SDK for AppConfig module holds the client classes that are used for + communicating with AppConfig. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.appconfig + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/appconfig/src/main/resources/codegen-resources/paginators-1.json b/services/appconfig/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..6a79ddb03f74 --- /dev/null +++ b/services/appconfig/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,29 @@ +{ + "pagination": { + "ListApplications": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListConfigurationProfiles": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDeploymentStrategies": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDeployments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListEnvironments": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/appconfig/src/main/resources/codegen-resources/service-2.json b/services/appconfig/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..e4353582bc71 --- /dev/null +++ b/services/appconfig/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,1870 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-10-09", + "endpointPrefix":"appconfig", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"AppConfig", + "serviceFullName":"Amazon AppConfig", + "serviceId":"AppConfig", + "signatureVersion":"v4", + "signingName":"appconfig", + "uid":"appconfig-2019-10-09" + }, + "operations":{ + "CreateApplication":{ + "name":"CreateApplication", + "http":{ + "method":"POST", + "requestUri":"/applications", + "responseCode":201 + }, + "input":{"shape":"CreateApplicationRequest"}, + "output":{"shape":"Application"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    An application in AppConfig is a logical unit of code that provides capabilities for your customers. For example, an application can be a microservice that runs on Amazon EC2 instances, a mobile application installed by your users, a serverless application using Amazon API Gateway and AWS Lambda, or any system you run on behalf of others.

    " + }, + "CreateConfigurationProfile":{ + "name":"CreateConfigurationProfile", + "http":{ + "method":"POST", + "requestUri":"/applications/{ApplicationId}/configurationprofiles", + "responseCode":201 + }, + "input":{"shape":"CreateConfigurationProfileRequest"}, + "output":{"shape":"ConfigurationProfile"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Information that enables AppConfig to access the configuration source. Valid configuration sources include Systems Manager (SSM) documents, SSM Parameter Store parameters, and Amazon S3 objects. A configuration profile includes the following information.

    • The Uri location of the configuration data.

    • The AWS Identity and Access Management (IAM) role that provides access to the configuration data.

    • A validator for the configuration data. Available validators include either a JSON Schema or an AWS Lambda function.

    For more information, see Create a Configuration and a Configuration Profile in the AWS AppConfig User Guide.

    " + }, + "CreateDeploymentStrategy":{ + "name":"CreateDeploymentStrategy", + "http":{ + "method":"POST", + "requestUri":"/deploymentstrategies", + "responseCode":201 + }, + "input":{"shape":"CreateDeploymentStrategyRequest"}, + "output":{"shape":"DeploymentStrategy"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    A deployment strategy defines important criteria for rolling out your configuration to the designated targets. A deployment strategy includes: the overall duration required, a percentage of targets to receive the deployment during each interval, an algorithm that defines how percentage grows, and bake time.

    " + }, + "CreateEnvironment":{ + "name":"CreateEnvironment", + "http":{ + "method":"POST", + "requestUri":"/applications/{ApplicationId}/environments", + "responseCode":201 + }, + "input":{"shape":"CreateEnvironmentRequest"}, + "output":{"shape":"Environment"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    For each application, you define one or more environments. An environment is a logical deployment group of AppConfig targets, such as applications in a Beta or Production environment. You can also define environments for application subcomponents such as the Web, Mobile and Back-end components for your application. You can configure Amazon CloudWatch alarms for each environment. The system monitors alarms during a configuration deployment. If an alarm is triggered, the system rolls back the configuration.

    " + }, + "DeleteApplication":{ + "name":"DeleteApplication", + "http":{ + "method":"DELETE", + "requestUri":"/applications/{ApplicationId}", + "responseCode":204 + }, + "input":{"shape":"DeleteApplicationRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Delete an application. Deleting an application does not delete a configuration from a host.

    " + }, + "DeleteConfigurationProfile":{ + "name":"DeleteConfigurationProfile", + "http":{ + "method":"DELETE", + "requestUri":"/applications/{ApplicationId}/configurationprofiles/{ConfigurationProfileId}", + "responseCode":204 + }, + "input":{"shape":"DeleteConfigurationProfileRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Delete a configuration profile. Deleting a configuration profile does not delete a configuration from a host.

    " + }, + "DeleteDeploymentStrategy":{ + "name":"DeleteDeploymentStrategy", + "http":{ + "method":"DELETE", + "requestUri":"/deployementstrategies/{DeploymentStrategyId}", + "responseCode":204 + }, + "input":{"shape":"DeleteDeploymentStrategyRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Delete a deployment strategy. Deleting a deployment strategy does not delete a configuration from a host.

    " + }, + "DeleteEnvironment":{ + "name":"DeleteEnvironment", + "http":{ + "method":"DELETE", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}", + "responseCode":204 + }, + "input":{"shape":"DeleteEnvironmentRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Delete an environment. Deleting an environment does not delete a configuration from a host.

    " + }, + "GetApplication":{ + "name":"GetApplication", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}", + "responseCode":200 + }, + "input":{"shape":"GetApplicationRequest"}, + "output":{"shape":"Application"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about an application.

    " + }, + "GetConfiguration":{ + "name":"GetConfiguration", + "http":{ + "method":"GET", + "requestUri":"/applications/{Application}/environments/{Environment}/configurations/{Configuration}", + "responseCode":200 + }, + "input":{"shape":"GetConfigurationRequest"}, + "output":{"shape":"Configuration"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about a configuration.

    " + }, + "GetConfigurationProfile":{ + "name":"GetConfigurationProfile", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}/configurationprofiles/{ConfigurationProfileId}", + "responseCode":200 + }, + "input":{"shape":"GetConfigurationProfileRequest"}, + "output":{"shape":"ConfigurationProfile"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about a configuration profile.

    " + }, + "GetDeployment":{ + "name":"GetDeployment", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}/deployments/{DeploymentNumber}", + "responseCode":200 + }, + "input":{"shape":"GetDeploymentRequest"}, + "output":{"shape":"Deployment"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about a configuration deployment.

    " + }, + "GetDeploymentStrategy":{ + "name":"GetDeploymentStrategy", + "http":{ + "method":"GET", + "requestUri":"/deploymentstrategies/{DeploymentStrategyId}", + "responseCode":200 + }, + "input":{"shape":"GetDeploymentStrategyRequest"}, + "output":{"shape":"DeploymentStrategy"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about a deployment strategy. A deployment strategy defines important criteria for rolling out your configuration to the designated targets. A deployment strategy includes: the overall duration required, a percentage of targets to receive the deployment during each interval, an algorithm that defines how percentage grows, and bake time.

    " + }, + "GetEnvironment":{ + "name":"GetEnvironment", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}", + "responseCode":200 + }, + "input":{"shape":"GetEnvironmentRequest"}, + "output":{"shape":"Environment"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about an environment. An environment is a logical deployment group of AppConfig applications, such as applications in a Production environment or in an EU_Region environment. Each configuration deployment targets an environment. You can enable one or more Amazon CloudWatch alarms for an environment. If an alarm is triggered during a deployment, AppConfig roles back the configuration.

    " + }, + "ListApplications":{ + "name":"ListApplications", + "http":{ + "method":"GET", + "requestUri":"/applications", + "responseCode":200 + }, + "input":{"shape":"ListApplicationsRequest"}, + "output":{"shape":"Applications"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    List all applications in your AWS account.

    " + }, + "ListConfigurationProfiles":{ + "name":"ListConfigurationProfiles", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}/configurationprofiles", + "responseCode":200 + }, + "input":{"shape":"ListConfigurationProfilesRequest"}, + "output":{"shape":"ConfigurationProfiles"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Lists the configuration profiles for an application.

    " + }, + "ListDeploymentStrategies":{ + "name":"ListDeploymentStrategies", + "http":{ + "method":"GET", + "requestUri":"/deploymentstrategies", + "responseCode":200 + }, + "input":{"shape":"ListDeploymentStrategiesRequest"}, + "output":{"shape":"DeploymentStrategies"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    List deployment strategies.

    " + }, + "ListDeployments":{ + "name":"ListDeployments", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}/deployments", + "responseCode":200 + }, + "input":{"shape":"ListDeploymentsRequest"}, + "output":{"shape":"Deployments"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Lists the deployments for an environment.

    " + }, + "ListEnvironments":{ + "name":"ListEnvironments", + "http":{ + "method":"GET", + "requestUri":"/applications/{ApplicationId}/environments", + "responseCode":200 + }, + "input":{"shape":"ListEnvironmentsRequest"}, + "output":{"shape":"Environments"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    List the environments for an application.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"GET", + "requestUri":"/tags/{ResourceArn}", + "responseCode":200 + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ResourceTags"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"BadRequestException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Retrieves the list of key-value tags assigned to the resource.

    " + }, + "StartDeployment":{ + "name":"StartDeployment", + "http":{ + "method":"POST", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}/deployments", + "responseCode":201 + }, + "input":{"shape":"StartDeploymentRequest"}, + "output":{"shape":"Deployment"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Starts a deployment.

    " + }, + "StopDeployment":{ + "name":"StopDeployment", + "http":{ + "method":"DELETE", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}/deployments/{DeploymentNumber}", + "responseCode":202 + }, + "input":{"shape":"StopDeploymentRequest"}, + "output":{"shape":"Deployment"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Stops a deployment. This API action works only on deployments that have a status of DEPLOYING. This action moves the deployment to a status of ROLLED_BACK.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/tags/{ResourceArn}", + "responseCode":204 + }, + "input":{"shape":"TagResourceRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"BadRequestException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Metadata to assign to an AppConfig resource. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define. You can specify a maximum of 50 tags for a resource.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"DELETE", + "requestUri":"/tags/{ResourceArn}", + "responseCode":204 + }, + "input":{"shape":"UntagResourceRequest"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"BadRequestException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes a tag key and value from an AppConfig resource.

    " + }, + "UpdateApplication":{ + "name":"UpdateApplication", + "http":{ + "method":"PATCH", + "requestUri":"/applications/{ApplicationId}", + "responseCode":200 + }, + "input":{"shape":"UpdateApplicationRequest"}, + "output":{"shape":"Application"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates an application.

    " + }, + "UpdateConfigurationProfile":{ + "name":"UpdateConfigurationProfile", + "http":{ + "method":"PATCH", + "requestUri":"/applications/{ApplicationId}/configurationprofiles/{ConfigurationProfileId}", + "responseCode":200 + }, + "input":{"shape":"UpdateConfigurationProfileRequest"}, + "output":{"shape":"ConfigurationProfile"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates a configuration profile.

    " + }, + "UpdateDeploymentStrategy":{ + "name":"UpdateDeploymentStrategy", + "http":{ + "method":"PATCH", + "requestUri":"/deploymentstrategies/{DeploymentStrategyId}", + "responseCode":200 + }, + "input":{"shape":"UpdateDeploymentStrategyRequest"}, + "output":{"shape":"DeploymentStrategy"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates a deployment strategy.

    " + }, + "UpdateEnvironment":{ + "name":"UpdateEnvironment", + "http":{ + "method":"PATCH", + "requestUri":"/applications/{ApplicationId}/environments/{EnvironmentId}", + "responseCode":200 + }, + "input":{"shape":"UpdateEnvironmentRequest"}, + "output":{"shape":"Environment"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates an environment.

    " + }, + "ValidateConfiguration":{ + "name":"ValidateConfiguration", + "http":{ + "method":"POST", + "requestUri":"/applications/{ApplicationId}/configurationprofiles/{ConfigurationProfileId}/validators", + "responseCode":204 + }, + "input":{"shape":"ValidateConfigurationRequest"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Uses the validators in a configuration profile to validate a configuration.

    " + } + }, + "shapes":{ + "Application":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"Id", + "documentation":"

    The application ID.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The application name.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the application.

    " + } + } + }, + "ApplicationList":{ + "type":"list", + "member":{"shape":"Application"} + }, + "Applications":{ + "type":"structure", + "members":{ + "Items":{ + "shape":"ApplicationList", + "documentation":"

    The elements from this collection.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token for the next set of items to return. Use this token to get the next set of results.

    " + } + } + }, + "Arn":{ + "type":"string", + "max":2048, + "min":20, + "pattern":"arn:(aws[a-zA-Z-]*)?:[a-z]+:([a-z]{2}((-gov)|(-iso(b?)))?-[a-z]+-\\d{1})?:(\\d{12})?:[a-zA-Z0-9-_/:.]+" + }, + "BadRequestException":{ + "type":"structure", + "members":{ + "Message":{"shape":"String"} + }, + "documentation":"

    The input fails to satisfy the constraints specified by an AWS service.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "Blob":{"type":"blob"}, + "Configuration":{ + "type":"structure", + "members":{ + "Content":{ + "shape":"Blob", + "documentation":"

    The content of the configuration or the configuration data.

    " + }, + "ConfigurationVersion":{ + "shape":"Version", + "documentation":"

    The configuration version.

    ", + "location":"header", + "locationName":"Configuration-Version" + }, + "ContentType":{ + "shape":"String", + "documentation":"

    A standard MIME type describing the format of the configuration content. For more information, see Content-Type.

    ", + "location":"header", + "locationName":"Content-Type" + } + }, + "payload":"Content" + }, + "ConfigurationProfile":{ + "type":"structure", + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    " + }, + "Id":{ + "shape":"Id", + "documentation":"

    The configuration profile ID.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the configuration profile.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The configuration profile description.

    " + }, + "LocationUri":{ + "shape":"Uri", + "documentation":"

    The URI location of the configuration.

    " + }, + "RetrievalRoleArn":{ + "shape":"Arn", + "documentation":"

    The ARN of an IAM role with permission to access the configuration at the specified LocationUri.

    " + }, + "Validators":{ + "shape":"ValidatorList", + "documentation":"

    A list of methods for validating the configuration.

    " + } + } + }, + "ConfigurationProfileSummary":{ + "type":"structure", + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    " + }, + "Id":{ + "shape":"Id", + "documentation":"

    The ID of the configuration profile.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the configuration profile.

    " + }, + "LocationUri":{ + "shape":"Uri", + "documentation":"

    The URI location of the configuration.

    " + }, + "ValidatorTypes":{ + "shape":"ValidatorTypeList", + "documentation":"

    The types of validators in the configuration profile.

    " + } + }, + "documentation":"

    A summary of a configuration profile.

    " + }, + "ConfigurationProfileSummaryList":{ + "type":"list", + "member":{"shape":"ConfigurationProfileSummary"} + }, + "ConfigurationProfiles":{ + "type":"structure", + "members":{ + "Items":{ + "shape":"ConfigurationProfileSummaryList", + "documentation":"

    The elements from this collection.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token for the next set of items to return. Use this token to get the next set of results.

    " + } + } + }, + "ConflictException":{ + "type":"structure", + "members":{ + "Message":{"shape":"String"} + }, + "documentation":"

    The request could not be processed because of conflict in the current state of the resource.

    ", + "error":{"httpStatusCode":409}, + "exception":true + }, + "CreateApplicationRequest":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"Name", + "documentation":"

    A name for the application.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the application.

    " + }, + "Tags":{ + "shape":"TagMap", + "documentation":"

    Metadata to assign to the application. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define.

    " + } + } + }, + "CreateConfigurationProfileRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "Name", + "LocationUri", + "RetrievalRoleArn" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "Name":{ + "shape":"Name", + "documentation":"

    A name for the configuration profile.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the configuration profile.

    " + }, + "LocationUri":{ + "shape":"Uri", + "documentation":"

    A URI to locate the configuration. You can specify a Systems Manager (SSM) document, an SSM Parameter Store parameter, or an Amazon S3 object. For an SSM document, specify either the document name in the format ssm-document://<Document_name> or the Amazon Resource Name (ARN). For a parameter, specify either the parameter name in the format ssm-parameter://<Parameter_name> or the ARN. For an Amazon S3 object, specify the URI in the following format: s3://<bucket>/<objectKey> . Here is an example: s3://my-bucket/my-app/us-east-1/my-config.json

    " + }, + "RetrievalRoleArn":{ + "shape":"Arn", + "documentation":"

    The ARN of an IAM role with permission to access the configuration at the specified LocationUri.

    " + }, + "Validators":{ + "shape":"ValidatorList", + "documentation":"

    A list of methods for validating the configuration.

    " + }, + "Tags":{ + "shape":"TagMap", + "documentation":"

    Metadata to assign to the configuration profile. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define.

    " + } + } + }, + "CreateDeploymentStrategyRequest":{ + "type":"structure", + "required":[ + "Name", + "DeploymentDurationInMinutes", + "GrowthFactor", + "ReplicateTo" + ], + "members":{ + "Name":{ + "shape":"Name", + "documentation":"

    A name for the deployment strategy.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the deployment strategy.

    " + }, + "DeploymentDurationInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    Total amount of time for a deployment to last.

    ", + "box":true + }, + "FinalBakeTimeInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    The amount of time AppConfig monitors for alarms before considering the deployment to be complete and no longer eligible for automatic roll back.

    " + }, + "GrowthFactor":{ + "shape":"GrowthFactor", + "documentation":"

    The percentage of targets to receive a deployed configuration during each interval.

    ", + "box":true + }, + "GrowthType":{ + "shape":"GrowthType", + "documentation":"

    The algorithm used to define how percentage grows over time. AWS AppConfig supports the following growth types:

    Linear: For this type, AppConfig processes the deployment by dividing the total number of targets by the value specified for Step percentage. For example, a linear deployment that uses a Step percentage of 10 deploys the configuration to 10 percent of the hosts. After those deployments are complete, the system deploys the configuration to the next 10 percent. This continues until 100% of the targets have successfully received the configuration.

    Exponential: For this type, AppConfig processes the deployment exponentially using the following formula: G*(2^N). In this formula, G is the growth factor specified by the user and N is the number of steps until the configuration is deployed to all targets. For example, if you specify a growth factor of 2, then the system rolls out the configuration as follows:

    2*(2^0)

    2*(2^1)

    2*(2^2)

    Expressed numerically, the deployment rolls out as follows: 2% of the targets, 4% of the targets, 8% of the targets, and continues until the configuration has been deployed to all targets.

    " + }, + "ReplicateTo":{ + "shape":"ReplicateTo", + "documentation":"

    Save the deployment strategy to a Systems Manager (SSM) document.

    " + }, + "Tags":{ + "shape":"TagMap", + "documentation":"

    Metadata to assign to the deployment strategy. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define.

    " + } + } + }, + "CreateEnvironmentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "Name" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "Name":{ + "shape":"Name", + "documentation":"

    A name for the environment.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the environment.

    " + }, + "Monitors":{ + "shape":"MonitorList", + "documentation":"

    Amazon CloudWatch alarms to monitor during the deployment process.

    " + }, + "Tags":{ + "shape":"TagMap", + "documentation":"

    Metadata to assign to the environment. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define.

    " + } + } + }, + "DeleteApplicationRequest":{ + "type":"structure", + "required":["ApplicationId"], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The ID of the application to delete.

    ", + "location":"uri", + "locationName":"ApplicationId" + } + } + }, + "DeleteConfigurationProfileRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "ConfigurationProfileId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID that includes the configuration profile you want to delete.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "ConfigurationProfileId":{ + "shape":"Id", + "documentation":"

    The ID of the configuration profile you want to delete.

    ", + "location":"uri", + "locationName":"ConfigurationProfileId" + } + } + }, + "DeleteDeploymentStrategyRequest":{ + "type":"structure", + "required":["DeploymentStrategyId"], + "members":{ + "DeploymentStrategyId":{ + "shape":"DeploymentStrategyId", + "documentation":"

    The ID of the deployment strategy you want to delete.

    ", + "location":"uri", + "locationName":"DeploymentStrategyId" + } + } + }, + "DeleteEnvironmentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID that includes the environment you want to delete.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The ID of the environment you want to delete.

    ", + "location":"uri", + "locationName":"EnvironmentId" + } + } + }, + "Deployment":{ + "type":"structure", + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The ID of the application that was deployed.

    " + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The ID of the environment that was deployed.

    " + }, + "DeploymentStrategyId":{ + "shape":"Id", + "documentation":"

    The ID of the deployment strategy that was deployed.

    " + }, + "ConfigurationProfileId":{ + "shape":"Id", + "documentation":"

    The ID of the configuration profile that was deployed.

    " + }, + "DeploymentNumber":{ + "shape":"Integer", + "documentation":"

    The sequence number of the deployment.

    " + }, + "ConfigurationName":{ + "shape":"Name", + "documentation":"

    The name of the configuration.

    " + }, + "ConfigurationLocationUri":{ + "shape":"Uri", + "documentation":"

    Information about the source location of the configuration.

    " + }, + "ConfigurationVersion":{ + "shape":"Version", + "documentation":"

    The configuration version that was deployed.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the deployment.

    " + }, + "DeploymentDurationInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    Total amount of time the deployment lasted.

    " + }, + "GrowthType":{ + "shape":"GrowthType", + "documentation":"

    The algorithm used to define how percentage grew over time.

    " + }, + "GrowthFactor":{ + "shape":"Percentage", + "documentation":"

    The percentage of targets to receive a deployed configuration during each interval.

    " + }, + "FinalBakeTimeInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    The amount of time AppConfig monitored for alarms before considering the deployment to be complete and no longer eligible for automatic roll back.

    " + }, + "State":{ + "shape":"DeploymentState", + "documentation":"

    The state of the deployment.

    " + }, + "EventLog":{ + "shape":"DeploymentEvents", + "documentation":"

    A list containing all events related to a deployment. The most recent events are displayed first.

    " + }, + "PercentageComplete":{ + "shape":"Percentage", + "documentation":"

    The percentage of targets for which the deployment is available.

    " + }, + "StartedAt":{ + "shape":"Iso8601DateTime", + "documentation":"

    The time the deployment started.

    " + }, + "CompletedAt":{ + "shape":"Iso8601DateTime", + "documentation":"

    The time the deployment completed.

    " + } + } + }, + "DeploymentEvent":{ + "type":"structure", + "members":{ + "EventType":{ + "shape":"DeploymentEventType", + "documentation":"

    The type of deployment event. Deployment event types include the start, stop, or completion of a deployment; a percentage update; the start or stop of a bake period; the start or completion of a rollback.

    " + }, + "TriggeredBy":{ + "shape":"TriggeredBy", + "documentation":"

    The entity that triggered the deployment event. Events can be triggered by a user, AWS AppConfig, an Amazon CloudWatch alarm, or an internal error.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the deployment event. Descriptions include, but are not limited to, the user account or the CloudWatch alarm ARN that initiated a rollback, the percentage of hosts that received the deployment, or in the case of an internal error, a recommendation to attempt a new deployment.

    " + }, + "OccurredAt":{ + "shape":"Iso8601DateTime", + "documentation":"

    The date and time the event occurred.

    " + } + }, + "documentation":"

    An object that describes a deployment event.

    " + }, + "DeploymentEventType":{ + "type":"string", + "enum":[ + "PERCENTAGE_UPDATED", + "ROLLBACK_STARTED", + "ROLLBACK_COMPLETED", + "BAKE_TIME_STARTED", + "DEPLOYMENT_STARTED", + "DEPLOYMENT_COMPLETED" + ] + }, + "DeploymentEvents":{ + "type":"list", + "member":{"shape":"DeploymentEvent"} + }, + "DeploymentList":{ + "type":"list", + "member":{"shape":"DeploymentSummary"} + }, + "DeploymentState":{ + "type":"string", + "enum":[ + "BAKING", + "VALIDATING", + "DEPLOYING", + "COMPLETE", + "ROLLING_BACK", + "ROLLED_BACK" + ] + }, + "DeploymentStrategies":{ + "type":"structure", + "members":{ + "Items":{ + "shape":"DeploymentStrategyList", + "documentation":"

    The elements from this collection.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token for the next set of items to return. Use this token to get the next set of results.

    " + } + } + }, + "DeploymentStrategy":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"Id", + "documentation":"

    The deployment strategy ID.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the deployment strategy.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the deployment strategy.

    " + }, + "DeploymentDurationInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    Total amount of time the deployment lasted.

    " + }, + "GrowthType":{ + "shape":"GrowthType", + "documentation":"

    The algorithm used to define how percentage grew over time.

    " + }, + "GrowthFactor":{ + "shape":"Percentage", + "documentation":"

    The percentage of targets that received a deployed configuration during each interval.

    " + }, + "FinalBakeTimeInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    The amount of time AppConfig monitored for alarms before considering the deployment to be complete and no longer eligible for automatic roll back.

    " + }, + "ReplicateTo":{ + "shape":"ReplicateTo", + "documentation":"

    Save the deployment strategy to a Systems Manager (SSM) document.

    " + } + } + }, + "DeploymentStrategyId":{ + "type":"string", + "pattern":"([a-z0-9]{4,7}|arn:aws.*)" + }, + "DeploymentStrategyList":{ + "type":"list", + "member":{"shape":"DeploymentStrategy"} + }, + "DeploymentSummary":{ + "type":"structure", + "members":{ + "DeploymentNumber":{ + "shape":"Integer", + "documentation":"

    The sequence number of the deployment.

    " + }, + "ConfigurationName":{ + "shape":"Name", + "documentation":"

    The name of the configuration.

    " + }, + "ConfigurationVersion":{ + "shape":"Version", + "documentation":"

    The version of the configuration.

    " + }, + "DeploymentDurationInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    Total amount of time the deployment lasted.

    " + }, + "GrowthType":{ + "shape":"GrowthType", + "documentation":"

    The algorithm used to define how percentage grows over time.

    " + }, + "GrowthFactor":{ + "shape":"Percentage", + "documentation":"

    The percentage of targets to receive a deployed configuration during each interval.

    " + }, + "FinalBakeTimeInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    The amount of time AppConfig monitors for alarms before considering the deployment to be complete and no longer eligible for automatic roll back.

    " + }, + "State":{ + "shape":"DeploymentState", + "documentation":"

    The state of the deployment.

    " + }, + "PercentageComplete":{ + "shape":"Percentage", + "documentation":"

    The percentage of targets for which the deployment is available.

    " + }, + "StartedAt":{ + "shape":"Iso8601DateTime", + "documentation":"

    Time the deployment started.

    " + }, + "CompletedAt":{ + "shape":"Iso8601DateTime", + "documentation":"

    Time the deployment completed.

    " + } + }, + "documentation":"

    Information about the deployment.

    " + }, + "Deployments":{ + "type":"structure", + "members":{ + "Items":{ + "shape":"DeploymentList", + "documentation":"

    The elements from this collection.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token for the next set of items to return. Use this token to get the next set of results.

    " + } + } + }, + "Description":{ + "type":"string", + "max":1024, + "min":0 + }, + "Environment":{ + "type":"structure", + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    " + }, + "Id":{ + "shape":"Id", + "documentation":"

    The environment ID.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the environment.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the environment.

    " + }, + "State":{ + "shape":"EnvironmentState", + "documentation":"

    The state of the environment. An environment can be in one of the following states: READY_FOR_DEPLOYMENT, DEPLOYING, ROLLING_BACK, or ROLLED_BACK

    " + }, + "Monitors":{ + "shape":"MonitorList", + "documentation":"

    Amazon CloudWatch alarms monitored during the deployment.

    " + } + } + }, + "EnvironmentList":{ + "type":"list", + "member":{"shape":"Environment"} + }, + "EnvironmentState":{ + "type":"string", + "enum":[ + "READY_FOR_DEPLOYMENT", + "DEPLOYING", + "ROLLING_BACK", + "ROLLED_BACK" + ] + }, + "Environments":{ + "type":"structure", + "members":{ + "Items":{ + "shape":"EnvironmentList", + "documentation":"

    The elements from this collection.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token for the next set of items to return. Use this token to get the next set of results.

    " + } + } + }, + "GetApplicationRequest":{ + "type":"structure", + "required":["ApplicationId"], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The ID of the application you want to get.

    ", + "location":"uri", + "locationName":"ApplicationId" + } + } + }, + "GetConfigurationProfileRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "ConfigurationProfileId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The ID of the application that includes the configuration profile you want to get.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "ConfigurationProfileId":{ + "shape":"Id", + "documentation":"

    The ID of the configuration profile you want to get.

    ", + "location":"uri", + "locationName":"ConfigurationProfileId" + } + } + }, + "GetConfigurationRequest":{ + "type":"structure", + "required":[ + "Application", + "Environment", + "Configuration", + "ClientId" + ], + "members":{ + "Application":{ + "shape":"StringWithLengthBetween1And64", + "documentation":"

    The application to get. Specify either the application name or the application ID.

    ", + "location":"uri", + "locationName":"Application" + }, + "Environment":{ + "shape":"StringWithLengthBetween1And64", + "documentation":"

    The environment to get. Specify either the environment name or the environment ID.

    ", + "location":"uri", + "locationName":"Environment" + }, + "Configuration":{ + "shape":"StringWithLengthBetween1And64", + "documentation":"

    The configuration to get. Specify either the configuration name or the configuration ID.

    ", + "location":"uri", + "locationName":"Configuration" + }, + "ClientId":{ + "shape":"StringWithLengthBetween1And64", + "documentation":"

    A unique ID to identify the client for the configuration. This ID enables AppConfig to deploy the configuration in intervals, as defined in the deployment strategy.

    ", + "location":"querystring", + "locationName":"client_id" + }, + "ClientConfigurationVersion":{ + "shape":"Version", + "documentation":"

    The configuration version returned in the most recent GetConfiguration response.

    ", + "location":"querystring", + "locationName":"client_configuration_version" + } + } + }, + "GetDeploymentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId", + "DeploymentNumber" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The ID of the application that includes the deployment you want to get.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The ID of the environment that includes the deployment you want to get.

    ", + "location":"uri", + "locationName":"EnvironmentId" + }, + "DeploymentNumber":{ + "shape":"Integer", + "documentation":"

    The sequence number of the deployment.

    ", + "box":true, + "location":"uri", + "locationName":"DeploymentNumber" + } + } + }, + "GetDeploymentStrategyRequest":{ + "type":"structure", + "required":["DeploymentStrategyId"], + "members":{ + "DeploymentStrategyId":{ + "shape":"DeploymentStrategyId", + "documentation":"

    The ID of the deployment strategy to get.

    ", + "location":"uri", + "locationName":"DeploymentStrategyId" + } + } + }, + "GetEnvironmentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The ID of the application that includes the environment you want to get.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The ID of the environment you wnat to get.

    ", + "location":"uri", + "locationName":"EnvironmentId" + } + } + }, + "GrowthFactor":{ + "type":"float", + "max":100.0, + "min":1.0 + }, + "GrowthType":{ + "type":"string", + "enum":[ + "LINEAR", + "EXPONENTIAL" + ] + }, + "Id":{ + "type":"string", + "pattern":"[a-z0-9]{4,7}" + }, + "Integer":{"type":"integer"}, + "InternalServerException":{ + "type":"structure", + "members":{ + "Message":{"shape":"String"} + }, + "documentation":"

    There was an internal failure in the AppConfig service.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "Iso8601DateTime":{ + "type":"timestamp", + "timestampFormat":"iso8601" + }, + "ListApplicationsRequest":{ + "type":"structure", + "members":{ + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of items to return for this call. The call also returns a token that you can specify in a subsequent call to get the next set of results.

    ", + "box":true, + "location":"querystring", + "locationName":"max_results" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to start the list. Use this token to get the next set of results.

    ", + "location":"querystring", + "locationName":"next_token" + } + } + }, + "ListConfigurationProfilesRequest":{ + "type":"structure", + "required":["ApplicationId"], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of items to return for this call. The call also returns a token that you can specify in a subsequent call to get the next set of results.

    ", + "box":true, + "location":"querystring", + "locationName":"max_results" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to start the list. Use this token to get the next set of results.

    ", + "location":"querystring", + "locationName":"next_token" + } + } + }, + "ListDeploymentStrategiesRequest":{ + "type":"structure", + "members":{ + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of items to return for this call. The call also returns a token that you can specify in a subsequent call to get the next set of results.

    ", + "box":true, + "location":"querystring", + "locationName":"max_results" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to start the list. Use this token to get the next set of results.

    ", + "location":"querystring", + "locationName":"next_token" + } + } + }, + "ListDeploymentsRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The environment ID.

    ", + "location":"uri", + "locationName":"EnvironmentId" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of items to return for this call. The call also returns a token that you can specify in a subsequent call to get the next set of results.

    ", + "box":true, + "location":"querystring", + "locationName":"max_results" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to start the list. Use this token to get the next set of results.

    ", + "location":"querystring", + "locationName":"next_token" + } + } + }, + "ListEnvironmentsRequest":{ + "type":"structure", + "required":["ApplicationId"], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of items to return for this call. The call also returns a token that you can specify in a subsequent call to get the next set of results.

    ", + "box":true, + "location":"querystring", + "locationName":"max_results" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to start the list. Use this token to get the next set of results.

    ", + "location":"querystring", + "locationName":"next_token" + } + } + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"Arn", + "documentation":"

    The resource ARN.

    ", + "location":"uri", + "locationName":"ResourceArn" + } + } + }, + "MaxResults":{ + "type":"integer", + "max":50, + "min":1 + }, + "MinutesBetween0And24Hours":{ + "type":"integer", + "max":1440, + "min":0 + }, + "Monitor":{ + "type":"structure", + "members":{ + "AlarmArn":{ + "shape":"Arn", + "documentation":"

    ARN of the Amazon CloudWatch alarm.

    " + }, + "AlarmRoleArn":{ + "shape":"Arn", + "documentation":"

    ARN of an IAM role for AppConfig to monitor AlarmArn.

    " + } + }, + "documentation":"

    Amazon CloudWatch alarms to monitor during the deployment process.

    " + }, + "MonitorList":{ + "type":"list", + "member":{"shape":"Monitor"}, + "max":5, + "min":0 + }, + "Name":{ + "type":"string", + "max":64, + "min":1 + }, + "NextToken":{ + "type":"string", + "max":2048, + "min":1 + }, + "Percentage":{ + "type":"float", + "max":100.0, + "min":1.0 + }, + "ReplicateTo":{ + "type":"string", + "enum":[ + "NONE", + "SSM_DOCUMENT" + ] + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"String"}, + "ResourceName":{"shape":"String"} + }, + "documentation":"

    The requested resource could not be found.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "ResourceTags":{ + "type":"structure", + "members":{ + "Tags":{ + "shape":"TagMap", + "documentation":"

    Metadata to assign to AppConfig resources. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define.

    " + } + } + }, + "StartDeploymentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId", + "DeploymentStrategyId", + "ConfigurationProfileId", + "ConfigurationVersion" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The environment ID.

    ", + "location":"uri", + "locationName":"EnvironmentId" + }, + "DeploymentStrategyId":{ + "shape":"DeploymentStrategyId", + "documentation":"

    The deployment strategy ID.

    " + }, + "ConfigurationProfileId":{ + "shape":"Id", + "documentation":"

    The configuration profile ID.

    " + }, + "ConfigurationVersion":{ + "shape":"Version", + "documentation":"

    The configuration version to deploy.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the deployment.

    " + }, + "Tags":{ + "shape":"TagMap", + "documentation":"

    Metadata to assign to the deployment. Tags help organize and categorize your AppConfig resources. Each tag consists of a key and an optional value, both of which you define.

    " + } + } + }, + "StopDeploymentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId", + "DeploymentNumber" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The environment ID.

    ", + "location":"uri", + "locationName":"EnvironmentId" + }, + "DeploymentNumber":{ + "shape":"Integer", + "documentation":"

    The sequence number of the deployment.

    ", + "box":true, + "location":"uri", + "locationName":"DeploymentNumber" + } + } + }, + "String":{"type":"string"}, + "StringWithLengthBetween0And32768":{ + "type":"string", + "max":32768, + "min":0 + }, + "StringWithLengthBetween1And64":{ + "type":"string", + "max":64, + "min":1 + }, + "TagKey":{ + "type":"string", + "max":128, + "min":1 + }, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"}, + "max":50, + "min":0 + }, + "TagMap":{ + "type":"map", + "key":{"shape":"TagKey"}, + "value":{"shape":"TagValue"}, + "max":50, + "min":0 + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "Tags" + ], + "members":{ + "ResourceArn":{ + "shape":"Arn", + "documentation":"

    The ARN of the resource for which to retrieve tags.

    ", + "location":"uri", + "locationName":"ResourceArn" + }, + "Tags":{ + "shape":"TagMap", + "documentation":"

    The key-value string map. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

    " + } + } + }, + "TagValue":{ + "type":"string", + "max":256 + }, + "TriggeredBy":{ + "type":"string", + "enum":[ + "USER", + "APPCONFIG", + "CLOUDWATCH_ALARM", + "INTERNAL_ERROR" + ] + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "TagKeys" + ], + "members":{ + "ResourceArn":{ + "shape":"Arn", + "documentation":"

    The ARN of the resource for which to remove tags.

    ", + "location":"uri", + "locationName":"ResourceArn" + }, + "TagKeys":{ + "shape":"TagKeyList", + "documentation":"

    The tag keys to delete.

    ", + "location":"querystring", + "locationName":"tagKeys" + } + } + }, + "UpdateApplicationRequest":{ + "type":"structure", + "required":["ApplicationId"], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the application.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the application.

    " + } + } + }, + "UpdateConfigurationProfileRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "ConfigurationProfileId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "ConfigurationProfileId":{ + "shape":"Id", + "documentation":"

    The ID of the configuration profile.

    ", + "location":"uri", + "locationName":"ConfigurationProfileId" + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the configuration profile.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the configuration profile.

    " + }, + "RetrievalRoleArn":{ + "shape":"Arn", + "documentation":"

    The ARN of an IAM role with permission to access the configuration at the specified LocationUri.

    " + }, + "Validators":{ + "shape":"ValidatorList", + "documentation":"

    A list of methods for validating the configuration.

    " + } + } + }, + "UpdateDeploymentStrategyRequest":{ + "type":"structure", + "required":["DeploymentStrategyId"], + "members":{ + "DeploymentStrategyId":{ + "shape":"DeploymentStrategyId", + "documentation":"

    The deployment strategy ID.

    ", + "location":"uri", + "locationName":"DeploymentStrategyId" + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the deployment strategy.

    " + }, + "DeploymentDurationInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    Total amount of time for a deployment to last.

    ", + "box":true + }, + "FinalBakeTimeInMinutes":{ + "shape":"MinutesBetween0And24Hours", + "documentation":"

    The amount of time AppConfig monitors for alarms before considering the deployment to be complete and no longer eligible for automatic roll back.

    ", + "box":true + }, + "GrowthFactor":{ + "shape":"GrowthFactor", + "documentation":"

    The percentage of targets to receive a deployed configuration during each interval.

    ", + "box":true + }, + "GrowthType":{ + "shape":"GrowthType", + "documentation":"

    The algorithm used to define how percentage grows over time. AWS AppConfig supports the following growth types:

    Linear: For this type, AppConfig processes the deployment by increments of the growth factor evenly distributed over the deployment time. For example, a linear deployment that uses a growth factor of 20 initially makes the configuration available to 20 percent of the targets. After 1/5th of the deployment time has passed, the system updates the percentage to 40 percent. This continues until 100% of the targets are set to receive the deployed configuration.

    Exponential: For this type, AppConfig processes the deployment exponentially using the following formula: G*(2^N). In this formula, G is the growth factor specified by the user and N is the number of steps until the configuration is deployed to all targets. For example, if you specify a growth factor of 2, then the system rolls out the configuration as follows:

    2*(2^0)

    2*(2^1)

    2*(2^2)

    Expressed numerically, the deployment rolls out as follows: 2% of the targets, 4% of the targets, 8% of the targets, and continues until the configuration has been deployed to all targets.

    " + } + } + }, + "UpdateEnvironmentRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "EnvironmentId" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "EnvironmentId":{ + "shape":"Id", + "documentation":"

    The environment ID.

    ", + "location":"uri", + "locationName":"EnvironmentId" + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the environment.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the environment.

    " + }, + "Monitors":{ + "shape":"MonitorList", + "documentation":"

    Amazon CloudWatch alarms to monitor during the deployment process.

    " + } + } + }, + "Uri":{ + "type":"string", + "max":2048, + "min":1 + }, + "ValidateConfigurationRequest":{ + "type":"structure", + "required":[ + "ApplicationId", + "ConfigurationProfileId", + "ConfigurationVersion" + ], + "members":{ + "ApplicationId":{ + "shape":"Id", + "documentation":"

    The application ID.

    ", + "location":"uri", + "locationName":"ApplicationId" + }, + "ConfigurationProfileId":{ + "shape":"Id", + "documentation":"

    The configuration profile ID.

    ", + "location":"uri", + "locationName":"ConfigurationProfileId" + }, + "ConfigurationVersion":{ + "shape":"Version", + "documentation":"

    The version of the configuration to validate.

    ", + "location":"querystring", + "locationName":"configuration_version" + } + } + }, + "Validator":{ + "type":"structure", + "required":[ + "Type", + "Content" + ], + "members":{ + "Type":{ + "shape":"ValidatorType", + "documentation":"

    AppConfig supports validators of type JSON_SCHEMA and LAMBDA

    " + }, + "Content":{ + "shape":"StringWithLengthBetween0And32768", + "documentation":"

    Either the JSON Schema content or the Amazon Resource Name (ARN) of an AWS Lambda function.

    " + } + }, + "documentation":"

    A validator provides a syntactic or semantic check to ensure the configuration you want to deploy functions as intended. To validate your application configuration data, you provide a schema or a Lambda function that runs against the configuration. The configuration deployment or update can only proceed when the configuration data is valid.

    " + }, + "ValidatorList":{ + "type":"list", + "member":{"shape":"Validator"}, + "max":2, + "min":0 + }, + "ValidatorType":{ + "type":"string", + "enum":[ + "JSON_SCHEMA", + "LAMBDA" + ] + }, + "ValidatorTypeList":{ + "type":"list", + "member":{"shape":"ValidatorType"}, + "max":2, + "min":0 + }, + "Version":{ + "type":"string", + "max":128, + "min":1 + } + }, + "documentation":"AWS AppConfig

    Use AWS AppConfig, a capability of AWS Systems Manager, to create, manage, and quickly deploy application configurations. AppConfig supports controlled deployments to applications of any size and includes built-in validation checks and monitoring. You can use AppConfig with applications hosted on Amazon EC2 instances, AWS Lambda, containers, mobile applications, or IoT devices.

    To prevent errors when deploying application configurations, especially for production systems where a simple typo could cause an unexpected outage, AppConfig includes validators. A validator provides a syntactic or semantic check to ensure that the configuration you want to deploy works as intended. To validate your application configuration data, you provide a schema or a Lambda function that runs against the configuration. The configuration deployment or update can only proceed when the configuration data is valid.

    During a configuration deployment, AppConfig monitors the application to ensure that the deployment is successful. If the system encounters an error, AppConfig rolls back the change to minimize impact for your application users. You can configure a deployment strategy for each application or environment that includes deployment criteria, including velocity, bake time, and alarms to monitor. Similar to error monitoring, if a deployment triggers an alarm, AppConfig automatically rolls back to the previous version.

    AppConfig supports multiple use cases. Here are some examples.

    • Application tuning: Use AppConfig to carefully introduce changes to your application that can only be tested with production traffic.

    • Feature toggle: Use AppConfig to turn on new features that require a timely deployment, such as a product launch or announcement.

    • User membership: Use AppConfig to allow premium subscribers to access paid content.

    • Operational issues: Use AppConfig to reduce stress on your application when a dependency or other external factor impacts the system.

    This reference is intended to be used with the AWS AppConfig User Guide.

    " +} diff --git a/services/applicationautoscaling/build.properties b/services/applicationautoscaling/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/applicationautoscaling/build.properties +++ b/services/applicationautoscaling/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/applicationautoscaling/pom.xml b/services/applicationautoscaling/pom.xml index d92593cf5055..23502d714885 100644 --- a/services/applicationautoscaling/pom.xml +++ b/services/applicationautoscaling/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + codeguruprofiler + AWS Java SDK :: Services :: CodeGuruProfiler + The AWS Java SDK for CodeGuruProfiler module holds the client classes that are used for + communicating with CodeGuruProfiler. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.codeguruprofiler + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/codeguruprofiler/src/main/resources/codegen-resources/paginators-1.json b/services/codeguruprofiler/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..afbbca8aabb5 --- /dev/null +++ b/services/codeguruprofiler/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,14 @@ +{ + "pagination": { + "ListProfileTimes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListProfilingGroups": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/services/codeguruprofiler/src/main/resources/codegen-resources/service-2.json b/services/codeguruprofiler/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..f9f10049de00 --- /dev/null +++ b/services/codeguruprofiler/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,801 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-07-18", + "endpointPrefix":"codeguru-profiler", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceFullName":"Amazon CodeGuru Profiler", + "serviceId":"CodeGuruProfiler", + "signatureVersion":"v4", + "signingName":"codeguru-profiler", + "uid":"codeguruprofiler-2019-07-18" + }, + "operations":{ + "ConfigureAgent":{ + "name":"ConfigureAgent", + "http":{ + "method":"POST", + "requestUri":"/profilingGroups/{profilingGroupName}/configureAgent", + "responseCode":200 + }, + "input":{"shape":"ConfigureAgentRequest"}, + "output":{"shape":"ConfigureAgentResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    " + }, + "CreateProfilingGroup":{ + "name":"CreateProfilingGroup", + "http":{ + "method":"POST", + "requestUri":"/profilingGroups", + "responseCode":201 + }, + "input":{"shape":"CreateProfilingGroupRequest"}, + "output":{"shape":"CreateProfilingGroupResponse"}, + "errors":[ + {"shape":"ServiceQuotaExceededException"}, + {"shape":"InternalServerException"}, + {"shape":"ConflictException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates a profiling group.

    ", + "idempotent":true + }, + "DeleteProfilingGroup":{ + "name":"DeleteProfilingGroup", + "http":{ + "method":"DELETE", + "requestUri":"/profilingGroups/{profilingGroupName}", + "responseCode":204 + }, + "input":{"shape":"DeleteProfilingGroupRequest"}, + "output":{"shape":"DeleteProfilingGroupResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Deletes a profiling group.

    ", + "idempotent":true + }, + "DescribeProfilingGroup":{ + "name":"DescribeProfilingGroup", + "http":{ + "method":"GET", + "requestUri":"/profilingGroups/{profilingGroupName}", + "responseCode":200 + }, + "input":{"shape":"DescribeProfilingGroupRequest"}, + "output":{"shape":"DescribeProfilingGroupResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Describes a profiling group.

    " + }, + "GetProfile":{ + "name":"GetProfile", + "http":{ + "method":"GET", + "requestUri":"/profilingGroups/{profilingGroupName}/profile", + "responseCode":200 + }, + "input":{"shape":"GetProfileRequest"}, + "output":{"shape":"GetProfileResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Gets the aggregated profile of a profiling group for the specified time range. If the requested time range does not align with the available aggregated profiles, it is expanded to attain alignment. If aggregated profiles are available only for part of the period requested, the profile is returned from the earliest available to the latest within the requested time range.

    For example, if the requested time range is from 00:00 to 00:20 and the available profiles are from 00:15 to 00:25, the returned profile will be from 00:15 to 00:20.

    You must specify exactly two of the following parameters: startTime, period, and endTime.

    " + }, + "ListProfileTimes":{ + "name":"ListProfileTimes", + "http":{ + "method":"GET", + "requestUri":"/profilingGroups/{profilingGroupName}/profileTimes", + "responseCode":200 + }, + "input":{"shape":"ListProfileTimesRequest"}, + "output":{"shape":"ListProfileTimesResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    List the start times of the available aggregated profiles of a profiling group for an aggregation period within the specified time range.

    " + }, + "ListProfilingGroups":{ + "name":"ListProfilingGroups", + "http":{ + "method":"GET", + "requestUri":"/profilingGroups", + "responseCode":200 + }, + "input":{"shape":"ListProfilingGroupsRequest"}, + "output":{"shape":"ListProfilingGroupsResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Lists profiling groups.

    " + }, + "PostAgentProfile":{ + "name":"PostAgentProfile", + "http":{ + "method":"POST", + "requestUri":"/profilingGroups/{profilingGroupName}/agentProfile", + "responseCode":204 + }, + "input":{"shape":"PostAgentProfileRequest"}, + "output":{"shape":"PostAgentProfileResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    " + }, + "UpdateProfilingGroup":{ + "name":"UpdateProfilingGroup", + "http":{ + "method":"PUT", + "requestUri":"/profilingGroups/{profilingGroupName}", + "responseCode":200 + }, + "input":{"shape":"UpdateProfilingGroupRequest"}, + "output":{"shape":"UpdateProfilingGroupResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ConflictException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Updates a profiling group.

    ", + "idempotent":true + } + }, + "shapes":{ + "AgentConfiguration":{ + "type":"structure", + "required":[ + "periodInSeconds", + "shouldProfile" + ], + "members":{ + "periodInSeconds":{ + "shape":"Integer", + "documentation":"

    " + }, + "shouldProfile":{ + "shape":"Boolean", + "documentation":"

    " + } + }, + "documentation":"

    " + }, + "AgentOrchestrationConfig":{ + "type":"structure", + "required":["profilingEnabled"], + "members":{ + "profilingEnabled":{ + "shape":"Boolean", + "documentation":"

    " + } + }, + "documentation":"

    " + }, + "AgentProfile":{"type":"blob"}, + "AggregatedProfile":{"type":"blob"}, + "AggregatedProfileTime":{ + "type":"structure", + "members":{ + "period":{ + "shape":"AggregationPeriod", + "documentation":"

    The time period.

    " + }, + "start":{ + "shape":"Timestamp", + "documentation":"

    The start time.

    " + } + }, + "documentation":"

    Information about the time range of the latest available aggregated profile.

    " + }, + "AggregationPeriod":{ + "type":"string", + "enum":[ + "P1D", + "PT1H", + "PT5M" + ] + }, + "Boolean":{ + "type":"boolean", + "box":true + }, + "ClientToken":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^[\\w-]+$" + }, + "ConfigureAgentRequest":{ + "type":"structure", + "required":["profilingGroupName"], + "members":{ + "fleetInstanceId":{ + "shape":"FleetInstanceId", + "documentation":"

    " + }, + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    ", + "location":"uri", + "locationName":"profilingGroupName" + } + }, + "documentation":"

    The structure representing the configureAgentRequest.

    " + }, + "ConfigureAgentResponse":{ + "type":"structure", + "required":["configuration"], + "members":{ + "configuration":{ + "shape":"AgentConfiguration", + "documentation":"

    " + } + }, + "documentation":"

    The structure representing the configureAgentResponse.

    ", + "payload":"configuration" + }, + "ConflictException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    The requested operation would cause a conflict with the current state of a service resource associated with the request. Resolve the conflict before retrying this request.

    ", + "error":{ + "httpStatusCode":409, + "senderFault":true + }, + "exception":true + }, + "CreateProfilingGroupRequest":{ + "type":"structure", + "required":[ + "clientToken", + "profilingGroupName" + ], + "members":{ + "agentOrchestrationConfig":{ + "shape":"AgentOrchestrationConfig", + "documentation":"

    The agent orchestration configuration.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    Unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

    This parameter specifies a unique identifier for the new profiling group that helps ensure idempotency.

    ", + "idempotencyToken":true, + "location":"querystring", + "locationName":"clientToken" + }, + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    The name of the profiling group.

    " + } + }, + "documentation":"

    The structure representing the createProfiliingGroupRequest.

    " + }, + "CreateProfilingGroupResponse":{ + "type":"structure", + "required":["profilingGroup"], + "members":{ + "profilingGroup":{ + "shape":"ProfilingGroupDescription", + "documentation":"

    Information about the new profiling group

    " + } + }, + "documentation":"

    The structure representing the createProfilingGroupResponse.

    ", + "payload":"profilingGroup" + }, + "DeleteProfilingGroupRequest":{ + "type":"structure", + "required":["profilingGroupName"], + "members":{ + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    The profiling group name to delete.

    ", + "location":"uri", + "locationName":"profilingGroupName" + } + }, + "documentation":"

    The structure representing the deleteProfilingGroupRequest.

    " + }, + "DeleteProfilingGroupResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The structure representing the deleteProfilingGroupResponse.

    " + }, + "DescribeProfilingGroupRequest":{ + "type":"structure", + "required":["profilingGroupName"], + "members":{ + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    The profiling group name.

    ", + "location":"uri", + "locationName":"profilingGroupName" + } + }, + "documentation":"

    The structure representing the describeProfilingGroupRequest.

    " + }, + "DescribeProfilingGroupResponse":{ + "type":"structure", + "required":["profilingGroup"], + "members":{ + "profilingGroup":{ + "shape":"ProfilingGroupDescription", + "documentation":"

    Information about a profiling group.

    " + } + }, + "documentation":"

    The structure representing the describeProfilingGroupResponse.

    ", + "payload":"profilingGroup" + }, + "FleetInstanceId":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[\\w-.:/]+$" + }, + "GetProfileRequest":{ + "type":"structure", + "required":["profilingGroupName"], + "members":{ + "accept":{ + "shape":"String", + "documentation":"

    The format of the profile to return. You can choose application/json or the default application/x-amzn-ion.

    ", + "location":"header", + "locationName":"Accept" + }, + "endTime":{ + "shape":"Timestamp", + "documentation":"

    You must specify exactly two of the following parameters: startTime, period, and endTime.

    ", + "location":"querystring", + "locationName":"endTime" + }, + "maxDepth":{ + "shape":"MaxDepth", + "documentation":"

    The maximum depth of the graph.

    ", + "location":"querystring", + "locationName":"maxDepth" + }, + "period":{ + "shape":"Period", + "documentation":"

    The period of the profile to get. The time range must be in the past and not longer than one week.

    You must specify exactly two of the following parameters: startTime, period, and endTime.

    ", + "location":"querystring", + "locationName":"period" + }, + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    The name of the profiling group to get.

    ", + "location":"uri", + "locationName":"profilingGroupName" + }, + "startTime":{ + "shape":"Timestamp", + "documentation":"

    The start time of the profile to get.

    You must specify exactly two of the following parameters: startTime, period, and endTime.

    ", + "location":"querystring", + "locationName":"startTime" + } + }, + "documentation":"

    The structure representing the getProfileRequest.

    " + }, + "GetProfileResponse":{ + "type":"structure", + "required":[ + "contentType", + "profile" + ], + "members":{ + "contentEncoding":{ + "shape":"String", + "documentation":"

    The content encoding of the profile.

    ", + "location":"header", + "locationName":"Content-Encoding" + }, + "contentType":{ + "shape":"String", + "documentation":"

    The content type of the profile in the payload. It is either application/json or the default application/x-amzn-ion.

    ", + "location":"header", + "locationName":"Content-Type" + }, + "profile":{ + "shape":"AggregatedProfile", + "documentation":"

    Information about the profile.

    " + } + }, + "documentation":"

    The structure representing the getProfileResponse.

    ", + "payload":"profile" + }, + "Integer":{ + "type":"integer", + "box":true + }, + "InternalServerException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    The server encountered an internal error and is unable to complete the request.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "ListProfileTimesRequest":{ + "type":"structure", + "required":[ + "endTime", + "period", + "profilingGroupName", + "startTime" + ], + "members":{ + "endTime":{ + "shape":"Timestamp", + "documentation":"

    The end time of the time range from which to list the profiles.

    ", + "location":"querystring", + "locationName":"endTime" + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of profile time results returned by ListProfileTimes in paginated output. When this parameter is used, ListProfileTimes only returns maxResults results in a single page with a nextToken response element. The remaining results of the initial request can be seen by sending another ListProfileTimes request with the returned nextToken value.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The nextToken value returned from a previous paginated ListProfileTimes request where maxResults was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the nextToken value.

    This token should be treated as an opaque identifier that is only used to retrieve the next items in a list and not for other programmatic purposes.

    ", + "location":"querystring", + "locationName":"nextToken" + }, + "orderBy":{ + "shape":"OrderBy", + "documentation":"

    The order (ascending or descending by start time of the profile) to use when listing profiles. Defaults to TIMESTAMP_DESCENDING.

    ", + "location":"querystring", + "locationName":"orderBy" + }, + "period":{ + "shape":"AggregationPeriod", + "documentation":"

    The aggregation period.

    ", + "location":"querystring", + "locationName":"period" + }, + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    The name of the profiling group.

    ", + "location":"uri", + "locationName":"profilingGroupName" + }, + "startTime":{ + "shape":"Timestamp", + "documentation":"

    The start time of the time range from which to list the profiles.

    ", + "location":"querystring", + "locationName":"startTime" + } + }, + "documentation":"

    The structure representing the listProfileTimesRequest.

    " + }, + "ListProfileTimesResponse":{ + "type":"structure", + "required":["profileTimes"], + "members":{ + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The nextToken value to include in a future ListProfileTimes request. When the results of a ListProfileTimes request exceed maxResults, this value can be used to retrieve the next page of results. This value is null when there are no more results to return.

    " + }, + "profileTimes":{ + "shape":"ProfileTimes", + "documentation":"

    The list of start times of the available profiles for the aggregation period in the specified time range.

    " + } + }, + "documentation":"

    The structure representing the listProfileTimesResponse.

    " + }, + "ListProfilingGroupsRequest":{ + "type":"structure", + "members":{ + "includeDescription":{ + "shape":"Boolean", + "documentation":"

    A Boolean value indicating whether to include a description.

    ", + "location":"querystring", + "locationName":"includeDescription" + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of profiling groups results returned by ListProfilingGroups in paginated output. When this parameter is used, ListProfilingGroups only returns maxResults results in a single page along with a nextToken response element. The remaining results of the initial request can be seen by sending another ListProfilingGroups request with the returned nextToken value.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The nextToken value returned from a previous paginated ListProfilingGroups request where maxResults was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the nextToken value.

    This token should be treated as an opaque identifier that is only used to retrieve the next items in a list and not for other programmatic purposes.

    ", + "location":"querystring", + "locationName":"nextToken" + } + }, + "documentation":"

    The structure representing the listProfilingGroupsRequest.

    " + }, + "ListProfilingGroupsResponse":{ + "type":"structure", + "required":["profilingGroupNames"], + "members":{ + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The nextToken value to include in a future ListProfilingGroups request. When the results of a ListProfilingGroups request exceed maxResults, this value can be used to retrieve the next page of results. This value is null when there are no more results to return.

    " + }, + "profilingGroupNames":{ + "shape":"ProfilingGroupNames", + "documentation":"

    Information about profiling group names.

    " + }, + "profilingGroups":{ + "shape":"ProfilingGroupDescriptions", + "documentation":"

    Information about profiling groups.

    " + } + }, + "documentation":"

    The structure representing the listProfilingGroupsResponse.

    " + }, + "MaxDepth":{ + "type":"integer", + "box":true, + "max":10000, + "min":1 + }, + "MaxResults":{ + "type":"integer", + "box":true, + "max":1000, + "min":1 + }, + "OrderBy":{ + "type":"string", + "enum":[ + "TimestampAscending", + "TimestampDescending" + ] + }, + "PaginationToken":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^[\\w-]+$" + }, + "Period":{ + "type":"string", + "max":64, + "min":1 + }, + "PostAgentProfileRequest":{ + "type":"structure", + "required":[ + "agentProfile", + "contentType", + "profilingGroupName" + ], + "members":{ + "agentProfile":{ + "shape":"AgentProfile", + "documentation":"

    " + }, + "contentType":{ + "shape":"String", + "documentation":"

    ", + "location":"header", + "locationName":"Content-Type" + }, + "profileToken":{ + "shape":"ClientToken", + "documentation":"

    ", + "idempotencyToken":true, + "location":"querystring", + "locationName":"profileToken" + }, + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    ", + "location":"uri", + "locationName":"profilingGroupName" + } + }, + "documentation":"

    The structure representing the postAgentProfileRequest.

    ", + "payload":"agentProfile" + }, + "PostAgentProfileResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The structure representing the postAgentProfileResponse.

    " + }, + "ProfileTime":{ + "type":"structure", + "members":{ + "start":{ + "shape":"Timestamp", + "documentation":"

    The start time of the profile.

    " + } + }, + "documentation":"

    Information about the profile time.

    " + }, + "ProfileTimes":{ + "type":"list", + "member":{"shape":"ProfileTime"} + }, + "ProfilingGroupArn":{"type":"string"}, + "ProfilingGroupDescription":{ + "type":"structure", + "members":{ + "agentOrchestrationConfig":{ + "shape":"AgentOrchestrationConfig", + "documentation":"

    " + }, + "arn":{ + "shape":"ProfilingGroupArn", + "documentation":"

    The Amazon Resource Name (ARN) identifying the profiling group.

    " + }, + "createdAt":{ + "shape":"Timestamp", + "documentation":"

    The time, in milliseconds since the epoch, when the profiling group was created.

    " + }, + "name":{ + "shape":"ProfilingGroupName", + "documentation":"

    The name of the profiling group.

    " + }, + "profilingStatus":{ + "shape":"ProfilingStatus", + "documentation":"

    The status of the profiling group.

    " + }, + "updatedAt":{ + "shape":"Timestamp", + "documentation":"

    The time, in milliseconds since the epoch, when the profiling group was last updated.

    " + } + }, + "documentation":"

    The description of a profiling group.

    " + }, + "ProfilingGroupDescriptions":{ + "type":"list", + "member":{"shape":"ProfilingGroupDescription"} + }, + "ProfilingGroupName":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[\\w-]+$" + }, + "ProfilingGroupNames":{ + "type":"list", + "member":{"shape":"ProfilingGroupName"} + }, + "ProfilingStatus":{ + "type":"structure", + "members":{ + "latestAgentOrchestratedAt":{ + "shape":"Timestamp", + "documentation":"

    The time, in milliseconds since the epoch, when the latest agent was orchestrated.

    " + }, + "latestAgentProfileReportedAt":{ + "shape":"Timestamp", + "documentation":"

    The time, in milliseconds since the epoch, when the latest agent was reported..

    " + }, + "latestAggregatedProfile":{ + "shape":"AggregatedProfileTime", + "documentation":"

    The latest aggregated profile

    " + } + }, + "documentation":"

    Information about the profiling status.

    " + }, + "ResourceNotFoundException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    The resource specified in the request does not exist.

    ", + "error":{ + "httpStatusCode":404, + "senderFault":true + }, + "exception":true + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    You have exceeded your service quota. To perform the requested action, remove some of the relevant resources, or use Service Quotas to request a service quota increase.

    ", + "error":{ + "httpStatusCode":402, + "senderFault":true + }, + "exception":true + }, + "String":{"type":"string"}, + "ThrottlingException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    The request was denied due to request throttling.

    ", + "error":{ + "httpStatusCode":429, + "senderFault":true + }, + "exception":true + }, + "Timestamp":{ + "type":"timestamp", + "timestampFormat":"iso8601" + }, + "UpdateProfilingGroupRequest":{ + "type":"structure", + "required":[ + "agentOrchestrationConfig", + "profilingGroupName" + ], + "members":{ + "agentOrchestrationConfig":{ + "shape":"AgentOrchestrationConfig", + "documentation":"

    " + }, + "profilingGroupName":{ + "shape":"ProfilingGroupName", + "documentation":"

    The name of the profiling group to update.

    ", + "location":"uri", + "locationName":"profilingGroupName" + } + }, + "documentation":"

    The structure representing the updateProfilingGroupRequest.

    " + }, + "UpdateProfilingGroupResponse":{ + "type":"structure", + "required":["profilingGroup"], + "members":{ + "profilingGroup":{ + "shape":"ProfilingGroupDescription", + "documentation":"

    Updated information about the profiling group.

    " + } + }, + "documentation":"

    The structure representing the updateProfilingGroupResponse.

    ", + "payload":"profilingGroup" + }, + "ValidationException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    The parameter is not valid.

    ", + "error":{ + "httpStatusCode":400, + "senderFault":true + }, + "exception":true + } + }, + "documentation":"

    This section provides documentation for the Amazon CodeGuru Profiler API operations.

    " +} diff --git a/services/codegurureviewer/pom.xml b/services/codegurureviewer/pom.xml new file mode 100644 index 000000000000..9675d92d2892 --- /dev/null +++ b/services/codegurureviewer/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + codegurureviewer + AWS Java SDK :: Services :: CodeGuru Reviewer + The AWS Java SDK for CodeGuru Reviewer module holds the client classes that are used for + communicating with CodeGuru Reviewer. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.codegurureviewer + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/codegurureviewer/src/main/resources/codegen-resources/paginators-1.json b/services/codegurureviewer/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..bbc1f584fdd7 --- /dev/null +++ b/services/codegurureviewer/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListRepositoryAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "RepositoryAssociationSummaries" + } + } +} diff --git a/services/codegurureviewer/src/main/resources/codegen-resources/service-2.json b/services/codegurureviewer/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..f7c36673e07a --- /dev/null +++ b/services/codegurureviewer/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,447 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-09-19", + "endpointPrefix":"codeguru-reviewer", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"CodeGuruReviewer", + "serviceFullName":"Amazon CodeGuru Reviewer", + "serviceId":"CodeGuru Reviewer", + "signatureVersion":"v4", + "signingName":"codeguru-reviewer", + "uid":"codeguru-reviewer-2019-09-19" + }, + "operations":{ + "AssociateRepository":{ + "name":"AssociateRepository", + "http":{ + "method":"POST", + "requestUri":"/associations" + }, + "input":{"shape":"AssociateRepositoryRequest"}, + "output":{"shape":"AssociateRepositoryResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Associates an AWS CodeCommit repository with Amazon CodeGuru Reviewer. When you associate an AWS CodeCommit repository with Amazon CodeGuru Reviewer, Amazon CodeGuru Reviewer will provide recommendations for each pull request. You can view recommendations in the AWS CodeCommit repository.

    You can associate a GitHub repository using the Amazon CodeGuru Reviewer console.

    " + }, + "DescribeRepositoryAssociation":{ + "name":"DescribeRepositoryAssociation", + "http":{ + "method":"GET", + "requestUri":"/associations/{AssociationArn}" + }, + "input":{"shape":"DescribeRepositoryAssociationRequest"}, + "output":{"shape":"DescribeRepositoryAssociationResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Describes a repository association.

    " + }, + "DisassociateRepository":{ + "name":"DisassociateRepository", + "http":{ + "method":"DELETE", + "requestUri":"/associations/{AssociationArn}" + }, + "input":{"shape":"DisassociateRepositoryRequest"}, + "output":{"shape":"DisassociateRepositoryResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Removes the association between Amazon CodeGuru Reviewer and a repository.

    " + }, + "ListRepositoryAssociations":{ + "name":"ListRepositoryAssociations", + "http":{ + "method":"GET", + "requestUri":"/associations" + }, + "input":{"shape":"ListRepositoryAssociationsRequest"}, + "output":{"shape":"ListRepositoryAssociationsResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Lists repository associations. You can optionally filter on one or more of the following recommendation properties: provider types, states, names, and owners.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "error":{"httpStatusCode":403}, + "exception":true + }, + "Arn":{ + "type":"string", + "max":1600, + "min":1, + "pattern":"^arn:aws[^:\\s]*:codeguru-reviewer:[^:\\s]+:[\\d]{12}:[a-z]+:[\\w-]+$" + }, + "AssociateRepositoryRequest":{ + "type":"structure", + "required":["Repository"], + "members":{ + "Repository":{ + "shape":"Repository", + "documentation":"

    The repository to associate.

    " + }, + "ClientRequestToken":{ + "shape":"ClientRequestToken", + "documentation":"

    Unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

    If you want to add a new repository association, this parameter specifies a unique identifier for the new repository association that helps ensure idempotency.

    If you use the AWS CLI or one of the AWS SDK to call this operation, then you can leave this parameter empty. The CLI or SDK generates a random UUID for you and includes that in the request. If you don't use the SDK and instead generate a raw HTTP request to the Secrets Manager service endpoint, then you must generate a ClientRequestToken yourself for new versions and include that value in the request.

    You typically only need to interact with this value if you implement your own retry logic and want to ensure that a given repository association is not created twice. We recommend that you generate a UUID-type value to ensure uniqueness within the specified repository association.

    Amazon CodeGuru Reviewer uses this value to prevent the accidental creation of duplicate repository associations if there are failures and retries.

    ", + "idempotencyToken":true + } + } + }, + "AssociateRepositoryResponse":{ + "type":"structure", + "members":{ + "RepositoryAssociation":{ + "shape":"RepositoryAssociation", + "documentation":"

    Information about the repository association.

    " + } + } + }, + "AssociationId":{ + "type":"string", + "max":64, + "min":1 + }, + "ClientRequestToken":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^[\\w-]+$" + }, + "CodeCommitRepository":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"Name", + "documentation":"

    The name of the AWS CodeCommit repository.

    " + } + }, + "documentation":"

    Information about an AWS CodeCommit repository.

    " + }, + "ConflictException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The requested operation would cause a conflict with the current state of a service resource associated with the request. Resolve the conflict before retrying this request.

    ", + "error":{"httpStatusCode":409}, + "exception":true + }, + "DescribeRepositoryAssociationRequest":{ + "type":"structure", + "required":["AssociationArn"], + "members":{ + "AssociationArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) identifying the association.

    ", + "location":"uri", + "locationName":"AssociationArn" + } + } + }, + "DescribeRepositoryAssociationResponse":{ + "type":"structure", + "members":{ + "RepositoryAssociation":{ + "shape":"RepositoryAssociation", + "documentation":"

    Information about the repository association.

    " + } + } + }, + "DisassociateRepositoryRequest":{ + "type":"structure", + "required":["AssociationArn"], + "members":{ + "AssociationArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) identifying the association.

    ", + "location":"uri", + "locationName":"AssociationArn" + } + } + }, + "DisassociateRepositoryResponse":{ + "type":"structure", + "members":{ + "RepositoryAssociation":{ + "shape":"RepositoryAssociation", + "documentation":"

    Information about the disassociated repository.

    " + } + } + }, + "ErrorMessage":{"type":"string"}, + "InternalServerException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The server encountered an internal error and is unable to complete the request.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "ListRepositoryAssociationsRequest":{ + "type":"structure", + "members":{ + "ProviderTypes":{ + "shape":"ProviderTypes", + "documentation":"

    List of provider types to use as a filter.

    ", + "location":"querystring", + "locationName":"ProviderType" + }, + "States":{ + "shape":"RepositoryAssociationStates", + "documentation":"

    List of states to use as a filter.

    ", + "location":"querystring", + "locationName":"State" + }, + "Names":{ + "shape":"Names", + "documentation":"

    List of names to use as a filter.

    ", + "location":"querystring", + "locationName":"Name" + }, + "Owners":{ + "shape":"Owners", + "documentation":"

    List of owners to use as a filter. For AWS CodeCommit, the owner is the AWS account id. For GitHub, it is the GitHub account name.

    ", + "location":"querystring", + "locationName":"Owner" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of repository association results returned by ListRepositoryAssociations in paginated output. When this parameter is used, ListRepositoryAssociations only returns maxResults results in a single page along with a nextToken response element. The remaining results of the initial request can be seen by sending another ListRepositoryAssociations request with the returned nextToken value. This value can be between 1 and 100. If this parameter is not used, then ListRepositoryAssociations returns up to 100 results and a nextToken value if applicable.

    ", + "location":"querystring", + "locationName":"MaxResults" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The nextToken value returned from a previous paginated ListRepositoryAssociations request where maxResults was used and the results exceeded the value of that parameter. Pagination continues from the end of the previous results that returned the nextToken value.

    This token should be treated as an opaque identifier that is only used to retrieve the next items in a list and not for other programmatic purposes.

    ", + "location":"querystring", + "locationName":"NextToken" + } + } + }, + "ListRepositoryAssociationsResponse":{ + "type":"structure", + "members":{ + "RepositoryAssociationSummaries":{ + "shape":"RepositoryAssociationSummaries", + "documentation":"

    A list of repository associations that meet the criteria of the request.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The nextToken value to include in a future ListRecommendations request. When the results of a ListRecommendations request exceed maxResults, this value can be used to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":100, + "min":1 + }, + "Name":{ + "type":"string", + "max":100, + "min":1 + }, + "Names":{ + "type":"list", + "member":{"shape":"Name"}, + "max":3, + "min":1 + }, + "NextToken":{ + "type":"string", + "max":2048, + "min":1 + }, + "NotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The resource specified in the request was not found.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "Owner":{ + "type":"string", + "max":100, + "min":1 + }, + "Owners":{ + "type":"list", + "member":{"shape":"Owner"}, + "max":3, + "min":1 + }, + "ProviderType":{ + "type":"string", + "enum":[ + "CodeCommit", + "GitHub" + ] + }, + "ProviderTypes":{ + "type":"list", + "member":{"shape":"ProviderType"}, + "max":3, + "min":1 + }, + "Repository":{ + "type":"structure", + "members":{ + "CodeCommit":{ + "shape":"CodeCommitRepository", + "documentation":"

    Information about an AWS CodeCommit repository.

    " + } + }, + "documentation":"

    Information about a repository.

    " + }, + "RepositoryAssociation":{ + "type":"structure", + "members":{ + "AssociationId":{ + "shape":"AssociationId", + "documentation":"

    The id of the repository association.

    " + }, + "AssociationArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) identifying the repository association.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the repository.

    " + }, + "Owner":{ + "shape":"Owner", + "documentation":"

    The owner of the repository.

    " + }, + "ProviderType":{ + "shape":"ProviderType", + "documentation":"

    The provider type of the repository association.

    " + }, + "State":{ + "shape":"RepositoryAssociationState", + "documentation":"

    The state of the repository association.

    " + }, + "StateReason":{ + "shape":"StateReason", + "documentation":"

    A description of why the repository association is in the current state.

    " + }, + "LastUpdatedTimeStamp":{ + "shape":"TimeStamp", + "documentation":"

    The time, in milliseconds since the epoch, when the repository association was last updated.

    " + }, + "CreatedTimeStamp":{ + "shape":"TimeStamp", + "documentation":"

    The time, in milliseconds since the epoch, when the repository association was created.

    " + } + }, + "documentation":"

    Information about a repository association.

    " + }, + "RepositoryAssociationState":{ + "type":"string", + "enum":[ + "Associated", + "Associating", + "Failed", + "Disassociating" + ] + }, + "RepositoryAssociationStates":{ + "type":"list", + "member":{"shape":"RepositoryAssociationState"}, + "max":3, + "min":1 + }, + "RepositoryAssociationSummaries":{ + "type":"list", + "member":{"shape":"RepositoryAssociationSummary"} + }, + "RepositoryAssociationSummary":{ + "type":"structure", + "members":{ + "AssociationArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) identifying the repository association.

    " + }, + "LastUpdatedTimeStamp":{ + "shape":"TimeStamp", + "documentation":"

    The time, in milliseconds since the epoch, since the repository association was last updated.

    " + }, + "AssociationId":{ + "shape":"AssociationId", + "documentation":"

    The repository association ID.

    " + }, + "Name":{ + "shape":"Name", + "documentation":"

    The name of the repository association.

    " + }, + "Owner":{ + "shape":"Owner", + "documentation":"

    The owner of the repository association.

    " + }, + "ProviderType":{ + "shape":"ProviderType", + "documentation":"

    The provider type of the repository association.

    " + }, + "State":{ + "shape":"RepositoryAssociationState", + "documentation":"

    The state of the repository association.

    Associated

    Amazon CodeGuru Reviewer is associated with the repository.

    Associating

    The association is in progress.

    Failed

    The association failed. For more information about troubleshooting (or why it failed), see [troubleshooting topic].

    Disassociating

    Amazon CodeGuru Reviewer is in the process of disassociating with the repository.

    " + } + }, + "documentation":"

    Information about a repository association.

    " + }, + "StateReason":{ + "type":"string", + "max":256, + "min":0 + }, + "ThrottlingException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request was denied due to request throttling.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "TimeStamp":{"type":"timestamp"}, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The input fails to satisfy the specified constraints.

    ", + "error":{"httpStatusCode":400}, + "exception":true + } + }, + "documentation":"

    This section provides documentation for the Amazon CodeGuru Reviewer API operations.

    " +} diff --git a/services/codepipeline/build.properties b/services/codepipeline/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/codepipeline/build.properties +++ b/services/codepipeline/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/codepipeline/pom.xml b/services/codepipeline/pom.xml index d3a9debbc714..765e8524ca62 100644 --- a/services/codepipeline/pom.xml +++ b/services/codepipeline/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + codestarconnections + AWS Java SDK :: Services :: CodeStar connections + The AWS Java SDK for CodeStar connections module holds the client classes that are used for + communicating with CodeStar connections. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.codestarconnections + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/codestarconnections/src/main/resources/codegen-resources/paginators-1.json b/services/codestarconnections/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..deffa71f04db --- /dev/null +++ b/services/codestarconnections/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "ListConnections": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/codestarconnections/src/main/resources/codegen-resources/service-2.json b/services/codestarconnections/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..9116e8ecc3fe --- /dev/null +++ b/services/codestarconnections/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,250 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-12-01", + "endpointPrefix":"codestar-connections", + "jsonVersion":"1.0", + "protocol":"json", + "serviceFullName":"AWS CodeStar connections", + "serviceId":"CodeStar connections", + "signatureVersion":"v4", + "signingName":"codestar-connections", + "targetPrefix":"com.amazonaws.codestar.connections.CodeStar_connections_20191201", + "uid":"codestar-connections-2019-12-01" + }, + "operations":{ + "CreateConnection":{ + "name":"CreateConnection", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateConnectionInput"}, + "output":{"shape":"CreateConnectionOutput"}, + "errors":[ + {"shape":"LimitExceededException"} + ], + "documentation":"

    Creates a connection that can then be given to other AWS services like CodePipeline so that it can access third-party code repositories. The connection is in pending status until the third-party connection handshake is completed from the console.

    " + }, + "DeleteConnection":{ + "name":"DeleteConnection", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteConnectionInput"}, + "output":{"shape":"DeleteConnectionOutput"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    The connection to be deleted.

    " + }, + "GetConnection":{ + "name":"GetConnection", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetConnectionInput"}, + "output":{"shape":"GetConnectionOutput"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Returns the connection ARN and details such as status, owner, and provider type.

    " + }, + "ListConnections":{ + "name":"ListConnections", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListConnectionsInput"}, + "output":{"shape":"ListConnectionsOutput"}, + "documentation":"

    Lists the connections associated with your account.

    " + } + }, + "shapes":{ + "AccountId":{ + "type":"string", + "max":12, + "min":12, + "pattern":"[0-9]{12}" + }, + "Connection":{ + "type":"structure", + "members":{ + "ConnectionName":{ + "shape":"ConnectionName", + "documentation":"

    The name of the connection. Connection names must be unique in an AWS user account.

    " + }, + "ConnectionArn":{ + "shape":"ConnectionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the connection. The ARN is used as the connection reference when the connection is shared between AWS services.

    The ARN is never reused if the connection is deleted.

    " + }, + "ProviderType":{ + "shape":"ProviderType", + "documentation":"

    The name of the external provider where your third-party code repository is configured. Currently, the valid provider type is Bitbucket.

    " + }, + "OwnerAccountId":{ + "shape":"AccountId", + "documentation":"

    The name of the external provider where your third-party code repository is configured. For Bitbucket, this is the account ID of the owner of the Bitbucket repository.

    " + }, + "ConnectionStatus":{ + "shape":"ConnectionStatus", + "documentation":"

    The current status of the connection.

    " + } + }, + "documentation":"

    The configuration that allows a service such as CodePipeline to connect to a third-party code repository.

    " + }, + "ConnectionArn":{ + "type":"string", + "max":256, + "min":0, + "pattern":"arn:aws(-[\\w]+)*:.+:.+:[0-9]{12}:.+" + }, + "ConnectionList":{ + "type":"list", + "member":{"shape":"Connection"} + }, + "ConnectionName":{ + "type":"string", + "max":32, + "min":1 + }, + "ConnectionStatus":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "ERROR" + ] + }, + "CreateConnectionInput":{ + "type":"structure", + "required":[ + "ProviderType", + "ConnectionName" + ], + "members":{ + "ProviderType":{ + "shape":"ProviderType", + "documentation":"

    The name of the external provider where your third-party code repository is configured. Currently, the valid provider type is Bitbucket.

    " + }, + "ConnectionName":{ + "shape":"ConnectionName", + "documentation":"

    The name of the connection to be created. The name must be unique in the calling AWS account.

    " + } + } + }, + "CreateConnectionOutput":{ + "type":"structure", + "required":["ConnectionArn"], + "members":{ + "ConnectionArn":{ + "shape":"ConnectionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the connection to be created. The ARN is used as the connection reference when the connection is shared between AWS services.

    The ARN is never reused if the connection is deleted.

    " + } + } + }, + "DeleteConnectionInput":{ + "type":"structure", + "required":["ConnectionArn"], + "members":{ + "ConnectionArn":{ + "shape":"ConnectionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the connection to be deleted.

    The ARN is never reused if the connection is deleted.

    " + } + } + }, + "DeleteConnectionOutput":{ + "type":"structure", + "members":{ + } + }, + "ErrorMessage":{ + "type":"string", + "max":600 + }, + "GetConnectionInput":{ + "type":"structure", + "required":["ConnectionArn"], + "members":{ + "ConnectionArn":{ + "shape":"ConnectionArn", + "documentation":"

    The Amazon Resource Name (ARN) of a connection.

    " + } + } + }, + "GetConnectionOutput":{ + "type":"structure", + "members":{ + "Connection":{ + "shape":"Connection", + "documentation":"

    The connection details, such as status, owner, and provider type.

    " + } + } + }, + "LimitExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Exceeded the maximum limit for connections.

    ", + "exception":true + }, + "ListConnectionsInput":{ + "type":"structure", + "members":{ + "ProviderTypeFilter":{ + "shape":"ProviderType", + "documentation":"

    Filters the list of connections to those associated with a specified provider, such as Bitbucket.

    " + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return in a single call. To retrieve the remaining results, make another call with the returned nextToken value.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token that was returned from the previous ListConnections call, which can be used to return the next set of connections in the list.

    " + } + } + }, + "ListConnectionsOutput":{ + "type":"structure", + "members":{ + "Connections":{ + "shape":"ConnectionList", + "documentation":"

    A list of connections and the details for each connection, such as status, owner, and provider type.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that can be used in the next ListConnections call. To view all items in the list, continue to call this operation with each subsequent token until no more nextToken values are returned.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":50, + "min":1 + }, + "NextToken":{ + "type":"string", + "max":1024, + "min":1, + "pattern":"[a-zA-Z0-9=\\-\\\\/]+" + }, + "ProviderType":{ + "type":"string", + "enum":["Bitbucket"] + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Resource not found. Verify the connection resource ARN and try again.

    ", + "exception":true + } + }, + "documentation":"

    This AWS CodeStar Connections API Reference provides descriptions and usage examples of the operations and data types for the AWS CodeStar Connections API. You can use the Connections API to work with connections and installations.

    Connections are configurations that you use to connect AWS resources to external code repositories. Each connection is a resource that can be given to services such as CodePipeline to connect to a third-party repository such as Bitbucket. For example, you can add the connection in CodePipeline so that it triggers your pipeline when a code change is made to your third-party code repository. Each connection is named and associated with a unique ARN that is used to reference the connection.

    When you create a connection, the console initiates a third-party connection handshake. Installations are the apps that are used to conduct this handshake. For example, the installation for the Bitbucket provider type is the Bitbucket Cloud app. When you create a connection, you can choose an existing installation or create one.

    You can work with connections by calling:

    • CreateConnection, which creates a uniquely named connection that can be referenced by services such as CodePipeline.

    • DeleteConnection, which deletes the specified connection.

    • GetConnection, which returns information about the connection, including the connection status.

    • ListConnections, which lists the connections associated with your account.

    For information about how to use AWS CodeStar Connections, see the AWS CodePipeline User Guide.

    " +} diff --git a/services/codestarnotifications/pom.xml b/services/codestarnotifications/pom.xml new file mode 100644 index 000000000000..db0a9534e2bf --- /dev/null +++ b/services/codestarnotifications/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + codestarnotifications + AWS Java SDK :: Services :: Codestar Notifications + The AWS Java SDK for Codestar Notifications module holds the client classes that are used for + communicating with Codestar Notifications. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.codestarnotifications + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/codestarnotifications/src/main/resources/codegen-resources/paginators-1.json b/services/codestarnotifications/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..c958196e0843 --- /dev/null +++ b/services/codestarnotifications/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,22 @@ +{ + "pagination": { + "ListEventTypes": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "EventTypes" + }, + "ListNotificationRules": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "NotificationRules" + }, + "ListTargets": { + "input_token": "NextToken", + "limit_key": "MaxResults", + "output_token": "NextToken", + "result_key": "Targets" + } + } +} \ No newline at end of file diff --git a/services/codestarnotifications/src/main/resources/codegen-resources/service-2.json b/services/codestarnotifications/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..df94d9bcdccb --- /dev/null +++ b/services/codestarnotifications/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,973 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-10-15", + "endpointPrefix":"codestar-notifications", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceFullName":"AWS CodeStar Notifications", + "serviceId":"codestar notifications", + "signatureVersion":"v4", + "signingName":"codestar-notifications", + "uid":"codestar-notifications-2019-10-15" + }, + "operations":{ + "CreateNotificationRule":{ + "name":"CreateNotificationRule", + "http":{ + "method":"POST", + "requestUri":"/createNotificationRule" + }, + "input":{"shape":"CreateNotificationRuleRequest"}, + "output":{"shape":"CreateNotificationRuleResult"}, + "errors":[ + {"shape":"ResourceAlreadyExistsException"}, + {"shape":"ValidationException"}, + {"shape":"LimitExceededException"}, + {"shape":"ConfigurationException"}, + {"shape":"ConcurrentModificationException"}, + {"shape":"AccessDeniedException"} + ], + "documentation":"

    Creates a notification rule for a resource. The rule specifies the events you want notifications about and the targets (such as SNS topics) where you want to receive them.

    " + }, + "DeleteNotificationRule":{ + "name":"DeleteNotificationRule", + "http":{ + "method":"POST", + "requestUri":"/deleteNotificationRule" + }, + "input":{"shape":"DeleteNotificationRuleRequest"}, + "output":{"shape":"DeleteNotificationRuleResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"LimitExceededException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Deletes a notification rule for a resource.

    " + }, + "DeleteTarget":{ + "name":"DeleteTarget", + "http":{ + "method":"POST", + "requestUri":"/deleteTarget" + }, + "input":{"shape":"DeleteTargetRequest"}, + "output":{"shape":"DeleteTargetResult"}, + "errors":[ + {"shape":"ValidationException"} + ], + "documentation":"

    Deletes a specified target for notifications.

    " + }, + "DescribeNotificationRule":{ + "name":"DescribeNotificationRule", + "http":{ + "method":"POST", + "requestUri":"/describeNotificationRule" + }, + "input":{"shape":"DescribeNotificationRuleRequest"}, + "output":{"shape":"DescribeNotificationRuleResult"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns information about a specified notification rule.

    " + }, + "ListEventTypes":{ + "name":"ListEventTypes", + "http":{ + "method":"POST", + "requestUri":"/listEventTypes" + }, + "input":{"shape":"ListEventTypesRequest"}, + "output":{"shape":"ListEventTypesResult"}, + "errors":[ + {"shape":"InvalidNextTokenException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns information about the event types available for configuring notifications.

    " + }, + "ListNotificationRules":{ + "name":"ListNotificationRules", + "http":{ + "method":"POST", + "requestUri":"/listNotificationRules" + }, + "input":{"shape":"ListNotificationRulesRequest"}, + "output":{"shape":"ListNotificationRulesResult"}, + "errors":[ + {"shape":"InvalidNextTokenException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns a list of the notification rules for an AWS account.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"POST", + "requestUri":"/listTagsForResource" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResult"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns a list of the tags associated with a notification rule.

    " + }, + "ListTargets":{ + "name":"ListTargets", + "http":{ + "method":"POST", + "requestUri":"/listTargets" + }, + "input":{"shape":"ListTargetsRequest"}, + "output":{"shape":"ListTargetsResult"}, + "errors":[ + {"shape":"InvalidNextTokenException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns a list of the notification rule targets for an AWS account.

    " + }, + "Subscribe":{ + "name":"Subscribe", + "http":{ + "method":"POST", + "requestUri":"/subscribe" + }, + "input":{"shape":"SubscribeRequest"}, + "output":{"shape":"SubscribeResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Creates an association between a notification rule and an SNS topic so that the associated target can receive notifications when the events described in the rule are triggered.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/tagResource" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResult"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Associates a set of provided tags with a notification rule.

    " + }, + "Unsubscribe":{ + "name":"Unsubscribe", + "http":{ + "method":"POST", + "requestUri":"/unsubscribe" + }, + "input":{"shape":"UnsubscribeRequest"}, + "output":{"shape":"UnsubscribeResult"}, + "errors":[ + {"shape":"ValidationException"} + ], + "documentation":"

    Removes an association between a notification rule and an Amazon SNS topic so that subscribers to that topic stop receiving notifications when the events described in the rule are triggered.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"POST", + "requestUri":"/untagResource" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResult"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Removes the association between one or more provided tags and a notification rule.

    " + }, + "UpdateNotificationRule":{ + "name":"UpdateNotificationRule", + "http":{ + "method":"POST", + "requestUri":"/updateNotificationRule" + }, + "input":{"shape":"UpdateNotificationRuleRequest"}, + "output":{"shape":"UpdateNotificationRuleResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Updates a notification rule for a resource. You can change the events that trigger the notification rule, the status of the rule, and the targets that receive the notifications.

    To add or remove tags for a notification rule, you must use TagResource and UntagResource.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    AWS CodeStar Notifications can't create the notification rule because you do not have sufficient permissions.

    ", + "error":{"httpStatusCode":403}, + "exception":true + }, + "ClientRequestToken":{ + "type":"string", + "max":256, + "min":1, + "pattern":"^[\\w:/-]+$" + }, + "ConcurrentModificationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    AWS CodeStar Notifications can't complete the request because the resource is being modified by another process. Wait a few minutes and try again.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ConfigurationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    Some or all of the configuration is incomplete, missing, or not valid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "CreateNotificationRuleRequest":{ + "type":"structure", + "required":[ + "Name", + "EventTypeIds", + "Resource", + "Targets", + "DetailType" + ], + "members":{ + "Name":{ + "shape":"NotificationRuleName", + "documentation":"

    The name for the notification rule. Notifictaion rule names must be unique in your AWS account.

    " + }, + "EventTypeIds":{ + "shape":"EventTypeIds", + "documentation":"

    A list of event types associated with this notification rule. For a list of allowed events, see EventTypeSummary.

    " + }, + "Resource":{ + "shape":"NotificationRuleResource", + "documentation":"

    The Amazon Resource Name (ARN) of the resource to associate with the notification rule. Supported resources include pipelines in AWS CodePipeline, repositories in AWS CodeCommit, and build projects in AWS CodeBuild.

    " + }, + "Targets":{ + "shape":"Targets", + "documentation":"

    A list of Amazon Resource Names (ARNs) of SNS topics to associate with the notification rule.

    " + }, + "DetailType":{ + "shape":"DetailType", + "documentation":"

    The level of detail to include in the notifications for this resource. BASIC will include only the contents of the event as it would appear in AWS CloudWatch. FULL will include any supplemental information provided by AWS CodeStar Notifications and/or the service for the resource for which the notification is created.

    " + }, + "ClientRequestToken":{ + "shape":"ClientRequestToken", + "documentation":"

    A unique, client-generated idempotency token that, when provided in a request, ensures the request cannot be repeated with a changed parameter. If a request with the same parameters is received and a token is included, the request returns information about the initial request that used that token.

    The AWS SDKs prepopulate client request tokens. If you are using an AWS SDK, an idempotency token is created for you.

    ", + "idempotencyToken":true + }, + "Tags":{ + "shape":"Tags", + "documentation":"

    A list of tags to apply to this notification rule. Key names cannot start with \"aws\".

    " + }, + "Status":{ + "shape":"NotificationRuleStatus", + "documentation":"

    The status of the notification rule. The default value is ENABLED. If the status is set to DISABLED, notifications aren't sent for the notification rule.

    " + } + } + }, + "CreateNotificationRuleResult":{ + "type":"structure", + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule.

    " + } + } + }, + "CreatedTimestamp":{"type":"timestamp"}, + "DeleteNotificationRuleRequest":{ + "type":"structure", + "required":["Arn"], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule you want to delete.

    " + } + } + }, + "DeleteNotificationRuleResult":{ + "type":"structure", + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the deleted notification rule.

    " + } + } + }, + "DeleteTargetRequest":{ + "type":"structure", + "required":["TargetAddress"], + "members":{ + "TargetAddress":{ + "shape":"TargetAddress", + "documentation":"

    The Amazon Resource Name (ARN) of the SNS topic to delete.

    " + }, + "ForceUnsubscribeAll":{ + "shape":"ForceUnsubscribeAll", + "documentation":"

    A Boolean value that can be used to delete all associations with this SNS topic. The default value is FALSE. If set to TRUE, all associations between that target and every notification rule in your AWS account are deleted.

    " + } + } + }, + "DeleteTargetResult":{ + "type":"structure", + "members":{ + } + }, + "DescribeNotificationRuleRequest":{ + "type":"structure", + "required":["Arn"], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule.

    " + } + } + }, + "DescribeNotificationRuleResult":{ + "type":"structure", + "required":["Arn"], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule.

    " + }, + "Name":{ + "shape":"NotificationRuleName", + "documentation":"

    The name of the notification rule.

    " + }, + "EventTypes":{ + "shape":"EventTypeBatch", + "documentation":"

    A list of the event types associated with the notification rule.

    " + }, + "Resource":{ + "shape":"NotificationRuleResource", + "documentation":"

    The Amazon Resource Name (ARN) of the resource associated with the notification rule.

    " + }, + "Targets":{ + "shape":"TargetsBatch", + "documentation":"

    A list of the SNS topics associated with the notification rule.

    " + }, + "DetailType":{ + "shape":"DetailType", + "documentation":"

    The level of detail included in the notifications for this resource. BASIC will include only the contents of the event as it would appear in AWS CloudWatch. FULL will include any supplemental information provided by AWS CodeStar Notifications and/or the service for the resource for which the notification is created.

    " + }, + "CreatedBy":{ + "shape":"NotificationRuleCreatedBy", + "documentation":"

    The name or email alias of the person who created the notification rule.

    " + }, + "Status":{ + "shape":"NotificationRuleStatus", + "documentation":"

    The status of the notification rule. Valid statuses are on (sending notifications) or off (not sending notifications).

    " + }, + "CreatedTimestamp":{ + "shape":"CreatedTimestamp", + "documentation":"

    The date and time the notification rule was created, in timestamp format.

    " + }, + "LastModifiedTimestamp":{ + "shape":"LastModifiedTimestamp", + "documentation":"

    The date and time the notification rule was most recently updated, in timestamp format.

    " + }, + "Tags":{ + "shape":"Tags", + "documentation":"

    The tags associated with the notification rule.

    " + } + } + }, + "DetailType":{ + "type":"string", + "enum":[ + "BASIC", + "FULL" + ] + }, + "EventTypeBatch":{ + "type":"list", + "member":{"shape":"EventTypeSummary"} + }, + "EventTypeId":{ + "type":"string", + "max":200, + "min":1 + }, + "EventTypeIds":{ + "type":"list", + "member":{"shape":"EventTypeId"} + }, + "EventTypeName":{"type":"string"}, + "EventTypeSummary":{ + "type":"structure", + "members":{ + "EventTypeId":{ + "shape":"EventTypeId", + "documentation":"

    The system-generated ID of the event.

    " + }, + "ServiceName":{ + "shape":"ServiceName", + "documentation":"

    The name of the service for which the event applies.

    " + }, + "EventTypeName":{ + "shape":"EventTypeName", + "documentation":"

    The name of the event.

    " + }, + "ResourceType":{ + "shape":"ResourceType", + "documentation":"

    The resource type of the event.

    " + } + }, + "documentation":"

    Returns information about an event that has triggered a notification rule.

    " + }, + "ForceUnsubscribeAll":{"type":"boolean"}, + "InvalidNextTokenException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    The value for the enumeration token used in the request to return the next batch of the results is not valid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "LastModifiedTimestamp":{"type":"timestamp"}, + "LimitExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    One of the AWS CodeStar Notifications limits has been exceeded. Limits apply to accounts, notification rules, notifications, resources, and targets. For more information, see Limits.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ListEventTypesFilter":{ + "type":"structure", + "required":[ + "Name", + "Value" + ], + "members":{ + "Name":{ + "shape":"ListEventTypesFilterName", + "documentation":"

    The system-generated name of the filter type you want to filter by.

    " + }, + "Value":{ + "shape":"ListEventTypesFilterValue", + "documentation":"

    The name of the resource type (for example, pipeline) or service name (for example, CodePipeline) that you want to filter by.

    " + } + }, + "documentation":"

    Information about a filter to apply to the list of returned event types. You can filter by resource type or service name.

    " + }, + "ListEventTypesFilterName":{ + "type":"string", + "enum":[ + "RESOURCE_TYPE", + "SERVICE_NAME" + ] + }, + "ListEventTypesFilterValue":{"type":"string"}, + "ListEventTypesFilters":{ + "type":"list", + "member":{"shape":"ListEventTypesFilter"} + }, + "ListEventTypesRequest":{ + "type":"structure", + "members":{ + "Filters":{ + "shape":"ListEventTypesFilters", + "documentation":"

    The filters to use to return information by service or resource type.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    An enumeration token that, when provided in a request, returns the next batch of the results.

    " + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    A non-negative integer used to limit the number of returned results. The default number is 50. The maximum number of results that can be returned is 100.

    ", + "box":true + } + } + }, + "ListEventTypesResult":{ + "type":"structure", + "members":{ + "EventTypes":{ + "shape":"EventTypeBatch", + "documentation":"

    Information about each event, including service name, resource type, event ID, and event name.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    An enumeration token that can be used in a request to return the next batch of the results.

    " + } + } + }, + "ListNotificationRulesFilter":{ + "type":"structure", + "required":[ + "Name", + "Value" + ], + "members":{ + "Name":{ + "shape":"ListNotificationRulesFilterName", + "documentation":"

    The name of the attribute you want to use to filter the returned notification rules.

    " + }, + "Value":{ + "shape":"ListNotificationRulesFilterValue", + "documentation":"

    The value of the attribute you want to use to filter the returned notification rules. For example, if you specify filtering by RESOURCE in Name, you might specify the ARN of a pipeline in AWS CodePipeline for the value.

    " + } + }, + "documentation":"

    Information about a filter to apply to the list of returned notification rules. You can filter by event type, owner, resource, or target.

    " + }, + "ListNotificationRulesFilterName":{ + "type":"string", + "enum":[ + "EVENT_TYPE_ID", + "CREATED_BY", + "RESOURCE", + "TARGET_ADDRESS" + ] + }, + "ListNotificationRulesFilterValue":{"type":"string"}, + "ListNotificationRulesFilters":{ + "type":"list", + "member":{"shape":"ListNotificationRulesFilter"} + }, + "ListNotificationRulesRequest":{ + "type":"structure", + "members":{ + "Filters":{ + "shape":"ListNotificationRulesFilters", + "documentation":"

    The filters to use to return information by service or resource type. For valid values, see ListNotificationRulesFilter.

    A filter with the same name can appear more than once when used with OR statements. Filters with different names should be applied with AND statements.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    An enumeration token that, when provided in a request, returns the next batch of the results.

    " + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    A non-negative integer used to limit the number of returned results. The maximum number of results that can be returned is 100.

    ", + "box":true + } + } + }, + "ListNotificationRulesResult":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    An enumeration token that can be used in a request to return the next batch of the results.

    " + }, + "NotificationRules":{ + "shape":"NotificationRuleBatch", + "documentation":"

    The list of notification rules for the AWS account, by Amazon Resource Name (ARN) and ID.

    " + } + } + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["Arn"], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) for the notification rule.

    " + } + } + }, + "ListTagsForResourceResult":{ + "type":"structure", + "members":{ + "Tags":{ + "shape":"Tags", + "documentation":"

    The tags associated with the notification rule.

    " + } + } + }, + "ListTargetsFilter":{ + "type":"structure", + "required":[ + "Name", + "Value" + ], + "members":{ + "Name":{ + "shape":"ListTargetsFilterName", + "documentation":"

    The name of the attribute you want to use to filter the returned targets.

    " + }, + "Value":{ + "shape":"ListTargetsFilterValue", + "documentation":"

    The value of the attribute you want to use to filter the returned targets. For example, if you specify SNS for the Target type, you could specify an Amazon Resource Name (ARN) for a topic as the value.

    " + } + }, + "documentation":"

    Information about a filter to apply to the list of returned targets. You can filter by target type, address, or status. For example, to filter results to notification rules that have active Amazon SNS topics as targets, you could specify a ListTargetsFilter Name as TargetType and a Value of SNS, and a Name of TARGET_STATUS and a Value of ACTIVE.

    " + }, + "ListTargetsFilterName":{ + "type":"string", + "enum":[ + "TARGET_TYPE", + "TARGET_ADDRESS", + "TARGET_STATUS" + ] + }, + "ListTargetsFilterValue":{"type":"string"}, + "ListTargetsFilters":{ + "type":"list", + "member":{"shape":"ListTargetsFilter"} + }, + "ListTargetsRequest":{ + "type":"structure", + "members":{ + "Filters":{ + "shape":"ListTargetsFilters", + "documentation":"

    The filters to use to return information by service or resource type. Valid filters include target type, target address, and target status.

    A filter with the same name can appear more than once when used with OR statements. Filters with different names should be applied with AND statements.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    An enumeration token that, when provided in a request, returns the next batch of the results.

    " + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    A non-negative integer used to limit the number of returned results. The maximum number of results that can be returned is 100.

    ", + "box":true + } + } + }, + "ListTargetsResult":{ + "type":"structure", + "members":{ + "Targets":{ + "shape":"TargetsBatch", + "documentation":"

    The list of notification rule targets.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    An enumeration token that can be used in a request to return the next batch of results.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":100, + "min":1 + }, + "Message":{ + "type":"string", + "min":1 + }, + "NextToken":{ + "type":"string", + "pattern":"^[\\w/+=]+$" + }, + "NotificationRuleArn":{ + "type":"string", + "pattern":"^arn:aws[^:\\s]*:codestar-notifications:[^:\\s]+:\\d{12}:notificationrule\\/(.*\\S)?$" + }, + "NotificationRuleBatch":{ + "type":"list", + "member":{"shape":"NotificationRuleSummary"} + }, + "NotificationRuleCreatedBy":{ + "type":"string", + "min":1 + }, + "NotificationRuleId":{ + "type":"string", + "max":40, + "min":1 + }, + "NotificationRuleName":{ + "type":"string", + "max":64, + "min":1, + "pattern":"[A-Za-z0-9\\-_ ]+$", + "sensitive":true + }, + "NotificationRuleResource":{ + "type":"string", + "pattern":"^arn:aws[^:\\s]*:[^:\\s]*:[^:\\s]*:[0-9]{12}:[^\\s]+$" + }, + "NotificationRuleStatus":{ + "type":"string", + "enum":[ + "ENABLED", + "DISABLED" + ] + }, + "NotificationRuleSummary":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"NotificationRuleId", + "documentation":"

    The unique ID of the notification rule.

    " + }, + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule.

    " + } + }, + "documentation":"

    Information about a specified notification rule.

    " + }, + "ResourceAlreadyExistsException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    A resource with the same name or ID already exists. Notification rule names must be unique in your AWS account.

    ", + "error":{"httpStatusCode":409}, + "exception":true + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    AWS CodeStar Notifications can't find a resource that matches the provided ARN.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "ResourceType":{ + "type":"string", + "min":1, + "pattern":"^([a-zA-Z0-9-])+$" + }, + "ServiceName":{"type":"string"}, + "SubscribeRequest":{ + "type":"structure", + "required":[ + "Arn", + "Target" + ], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule for which you want to create the association.

    " + }, + "Target":{"shape":"Target"}, + "ClientRequestToken":{ + "shape":"ClientRequestToken", + "documentation":"

    An enumeration token that, when provided in a request, returns the next batch of the results.

    " + } + } + }, + "SubscribeResult":{ + "type":"structure", + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule for which you have created assocations.

    " + } + } + }, + "TagKey":{ + "type":"string", + "max":128, + "min":1, + "pattern":"^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$" + }, + "TagKeys":{ + "type":"list", + "member":{"shape":"TagKey"} + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "Arn", + "Tags" + ], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule to tag.

    " + }, + "Tags":{ + "shape":"Tags", + "documentation":"

    The list of tags to associate with the resource. Tag key names cannot start with \"aws\".

    " + } + } + }, + "TagResourceResult":{ + "type":"structure", + "members":{ + "Tags":{ + "shape":"Tags", + "documentation":"

    The list of tags associated with the resource.

    " + } + } + }, + "TagValue":{ + "type":"string", + "max":256, + "pattern":"^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$" + }, + "Tags":{ + "type":"map", + "key":{"shape":"TagKey"}, + "value":{"shape":"TagValue"} + }, + "Target":{ + "type":"structure", + "members":{ + "TargetType":{ + "shape":"TargetType", + "documentation":"

    The target type. Can be an Amazon SNS topic.

    " + }, + "TargetAddress":{ + "shape":"TargetAddress", + "documentation":"

    The Amazon Resource Name (ARN) of the SNS topic.

    " + } + }, + "documentation":"

    Information about the SNS topics associated with a notification rule.

    " + }, + "TargetAddress":{ + "type":"string", + "max":320, + "min":1, + "sensitive":true + }, + "TargetStatus":{ + "type":"string", + "enum":[ + "PENDING", + "ACTIVE", + "UNREACHABLE", + "INACTIVE", + "DEACTIVATED" + ] + }, + "TargetSummary":{ + "type":"structure", + "members":{ + "TargetAddress":{ + "shape":"TargetAddress", + "documentation":"

    The Amazon Resource Name (ARN) of the SNS topic.

    " + }, + "TargetType":{ + "shape":"TargetType", + "documentation":"

    The type of the target (for example, SNS).

    " + }, + "TargetStatus":{ + "shape":"TargetStatus", + "documentation":"

    The status of the target.

    " + } + }, + "documentation":"

    Information about the targets specified for a notification rule.

    " + }, + "TargetType":{ + "type":"string", + "pattern":"^[A-Za-z]+$" + }, + "Targets":{ + "type":"list", + "member":{"shape":"Target"}, + "max":10 + }, + "TargetsBatch":{ + "type":"list", + "member":{"shape":"TargetSummary"} + }, + "UnsubscribeRequest":{ + "type":"structure", + "required":[ + "Arn", + "TargetAddress" + ], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule.

    " + }, + "TargetAddress":{ + "shape":"TargetAddress", + "documentation":"

    The ARN of the SNS topic to unsubscribe from the notification rule.

    " + } + } + }, + "UnsubscribeResult":{ + "type":"structure", + "required":["Arn"], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the the notification rule from which you have removed a subscription.

    " + } + } + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "Arn", + "TagKeys" + ], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule from which to remove the tags.

    " + }, + "TagKeys":{ + "shape":"TagKeys", + "documentation":"

    The key names of the tags to remove.

    " + } + } + }, + "UntagResourceResult":{ + "type":"structure", + "members":{ + } + }, + "UpdateNotificationRuleRequest":{ + "type":"structure", + "required":["Arn"], + "members":{ + "Arn":{ + "shape":"NotificationRuleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the notification rule.

    " + }, + "Name":{ + "shape":"NotificationRuleName", + "documentation":"

    The name of the notification rule.

    " + }, + "Status":{ + "shape":"NotificationRuleStatus", + "documentation":"

    The status of the notification rule. Valid statuses include enabled (sending notifications) or disabled (not sending notifications).

    " + }, + "EventTypeIds":{ + "shape":"EventTypeIds", + "documentation":"

    A list of event types associated with this notification rule.

    " + }, + "Targets":{ + "shape":"Targets", + "documentation":"

    The address and type of the targets to receive notifications from this notification rule.

    " + }, + "DetailType":{ + "shape":"DetailType", + "documentation":"

    The level of detail to include in the notifications for this resource. BASIC will include only the contents of the event as it would appear in AWS CloudWatch. FULL will include any supplemental information provided by AWS CodeStar Notifications and/or the service for the resource for which the notification is created.

    " + } + } + }, + "UpdateNotificationRuleResult":{ + "type":"structure", + "members":{ + } + }, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    One or more parameter values are not valid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + } + }, + "documentation":"

    This AWS CodeStar Notifications API Reference provides descriptions and usage examples of the operations and data types for the AWS CodeStar Notifications API. You can use the AWS CodeStar Notifications API to work with the following objects:

    Notification rules, by calling the following:

    Targets, by calling the following:

    • DeleteTarget, which removes a notification rule target (SNS topic) from a notification rule.

    • ListTargets, which lists the targets associated with a notification rule.

    Events, by calling the following:

    • ListEventTypes, which lists the event types you can include in a notification rule.

    Tags, by calling the following:

    • ListTagsForResource, which lists the tags already associated with a notification rule in your account.

    • TagResource, which associates a tag you provide with a notification rule in your account.

    • UntagResource, which removes a tag from a notification rule in your account.

    For information about how to use AWS CodeStar Notifications, see link in the CodeStarNotifications User Guide.

    " +} diff --git a/services/cognitoidentity/build.properties b/services/cognitoidentity/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/cognitoidentity/build.properties +++ b/services/cognitoidentity/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/cognitoidentity/pom.xml b/services/cognitoidentity/pom.xml index 112b99a4aac4..08880413f140 100644 --- a/services/cognitoidentity/pom.xml +++ b/services/cognitoidentity/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + computeoptimizer + AWS Java SDK :: Services :: Compute Optimizer + The AWS Java SDK for Compute Optimizer module holds the client classes that are used for + communicating with Compute Optimizer. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.computeoptimizer + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/computeoptimizer/src/main/resources/codegen-resources/paginators-1.json b/services/computeoptimizer/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..5677bd8e4a2d --- /dev/null +++ b/services/computeoptimizer/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,4 @@ +{ + "pagination": { + } +} diff --git a/services/computeoptimizer/src/main/resources/codegen-resources/service-2.json b/services/computeoptimizer/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..7a83d941dbde --- /dev/null +++ b/services/computeoptimizer/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,857 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-11-01", + "endpointPrefix":"compute-optimizer", + "jsonVersion":"1.0", + "protocol":"json", + "serviceFullName":"AWS Compute Optimizer", + "serviceId":"Compute Optimizer", + "signatureVersion":"v4", + "signingName":"compute-optimizer", + "targetPrefix":"ComputeOptimizerService", + "uid":"compute-optimizer-2019-11-01" + }, + "operations":{ + "GetAutoScalingGroupRecommendations":{ + "name":"GetAutoScalingGroupRecommendations", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetAutoScalingGroupRecommendationsRequest"}, + "output":{"shape":"GetAutoScalingGroupRecommendationsResponse"}, + "errors":[ + {"shape":"OptInRequiredException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"MissingAuthenticationToken"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns Auto Scaling group recommendations.

    AWS Compute Optimizer currently generates recommendations for Auto Scaling groups that are configured to run instances of the M, C, R, T, and X instance families. The service does not generate recommendations for Auto Scaling groups that have a scaling policy attached to them, or that do not have the same values for desired, minimum, and maximum capacity. In order for Compute Optimizer to analyze your Auto Scaling groups, they must be of a fixed size. For more information, see the AWS Compute Optimizer User Guide.

    " + }, + "GetEC2InstanceRecommendations":{ + "name":"GetEC2InstanceRecommendations", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetEC2InstanceRecommendationsRequest"}, + "output":{"shape":"GetEC2InstanceRecommendationsResponse"}, + "errors":[ + {"shape":"OptInRequiredException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"MissingAuthenticationToken"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns Amazon EC2 instance recommendations.

    AWS Compute Optimizer currently generates recommendations for Amazon Elastic Compute Cloud (Amazon EC2) and Amazon EC2 Auto Scaling. It generates recommendations for M, C, R, T, and X instance families. For more information, see the AWS Compute Optimizer User Guide.

    " + }, + "GetEC2RecommendationProjectedMetrics":{ + "name":"GetEC2RecommendationProjectedMetrics", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetEC2RecommendationProjectedMetricsRequest"}, + "output":{"shape":"GetEC2RecommendationProjectedMetricsResponse"}, + "errors":[ + {"shape":"OptInRequiredException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"MissingAuthenticationToken"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns the projected utilization metrics of Amazon EC2 instance recommendations.

    " + }, + "GetEnrollmentStatus":{ + "name":"GetEnrollmentStatus", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetEnrollmentStatusRequest"}, + "output":{"shape":"GetEnrollmentStatusResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"MissingAuthenticationToken"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns the enrollment (opt in) status of an account to the AWS Compute Optimizer service.

    If the account is a master account of an organization, this operation also confirms the enrollment status of member accounts within the organization.

    " + }, + "GetRecommendationSummaries":{ + "name":"GetRecommendationSummaries", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetRecommendationSummariesRequest"}, + "output":{"shape":"GetRecommendationSummariesResponse"}, + "errors":[ + {"shape":"OptInRequiredException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"MissingAuthenticationToken"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns the optimization findings for an account.

    For example, it returns the number of Amazon EC2 instances in an account that are under-provisioned, over-provisioned, or optimized. It also returns the number of Auto Scaling groups in an account that are not optimized, or optimized.

    " + }, + "UpdateEnrollmentStatus":{ + "name":"UpdateEnrollmentStatus", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateEnrollmentStatusRequest"}, + "output":{"shape":"UpdateEnrollmentStatusResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"MissingAuthenticationToken"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates the enrollment (opt in) status of an account to the AWS Compute Optimizer service.

    If the account is a master account of an organization, this operation can also enroll member accounts within the organization.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "exception":true, + "synthetic":true + }, + "AccountId":{"type":"string"}, + "AccountIds":{ + "type":"list", + "member":{"shape":"AccountId"} + }, + "AutoScalingGroupArn":{"type":"string"}, + "AutoScalingGroupArns":{ + "type":"list", + "member":{"shape":"AutoScalingGroupArn"} + }, + "AutoScalingGroupConfiguration":{ + "type":"structure", + "members":{ + "desiredCapacity":{ + "shape":"DesiredCapacity", + "documentation":"

    The desired capacity, or number of instances, for the Auto Scaling group.

    " + }, + "minSize":{ + "shape":"MinSize", + "documentation":"

    The minimum size, or minimum number of instances, for the Auto Scaling group.

    " + }, + "maxSize":{ + "shape":"MaxSize", + "documentation":"

    The maximum size, or maximum number of instances, for the Auto Scaling group.

    " + }, + "instanceType":{ + "shape":"InstanceType", + "documentation":"

    The instance type for the Auto Scaling group.

    " + } + }, + "documentation":"

    Describes the configuration of an Auto Scaling group.

    " + }, + "AutoScalingGroupName":{"type":"string"}, + "AutoScalingGroupRecommendation":{ + "type":"structure", + "members":{ + "accountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account ID of the Auto Scaling group.

    " + }, + "autoScalingGroupArn":{ + "shape":"AutoScalingGroupArn", + "documentation":"

    The Amazon Resource Name (ARN) of the Auto Scaling group.

    " + }, + "autoScalingGroupName":{ + "shape":"AutoScalingGroupName", + "documentation":"

    The name of the Auto Scaling group.

    " + }, + "finding":{ + "shape":"Finding", + "documentation":"

    The finding classification for the Auto Scaling group.

    Findings for Auto Scaling groups include:

    • NotOptimized —An Auto Scaling group is considered not optimized when AWS Compute Optimizer identifies a recommendation that can provide better performance for your workload.

    • Optimized —An Auto Scaling group is considered optimized when Compute Optimizer determines that the group is correctly provisioned to run your workload based on the chosen instance type. For optimized resources, Compute Optimizer might recommend a new generation instance type.

    The values that are returned might be NOT_OPTIMIZED or OPTIMIZED.

    " + }, + "utilizationMetrics":{ + "shape":"UtilizationMetrics", + "documentation":"

    An array of objects that describe the utilization metrics of the Auto Scaling group.

    " + }, + "lookBackPeriodInDays":{ + "shape":"LookBackPeriodInDays", + "documentation":"

    The number of days for which utilization metrics were analyzed for the Auto Scaling group.

    " + }, + "currentConfiguration":{ + "shape":"AutoScalingGroupConfiguration", + "documentation":"

    An array of objects that describe the current configuration of the Auto Scaling group.

    " + }, + "recommendationOptions":{ + "shape":"AutoScalingGroupRecommendationOptions", + "documentation":"

    An array of objects that describe the recommendation options for the Auto Scaling group.

    " + }, + "lastRefreshTimestamp":{ + "shape":"LastRefreshTimestamp", + "documentation":"

    The time stamp of when the Auto Scaling group recommendation was last refreshed.

    " + } + }, + "documentation":"

    Describes an Auto Scaling group recommendation.

    " + }, + "AutoScalingGroupRecommendationOption":{ + "type":"structure", + "members":{ + "configuration":{ + "shape":"AutoScalingGroupConfiguration", + "documentation":"

    An array of objects that describe an Auto Scaling group configuration.

    " + }, + "projectedUtilizationMetrics":{ + "shape":"ProjectedUtilizationMetrics", + "documentation":"

    An array of objects that describe the projected utilization metrics of the Auto Scaling group recommendation option.

    " + }, + "performanceRisk":{ + "shape":"PerformanceRisk", + "documentation":"

    The performance risk of the Auto Scaling group configuration recommendation.

    Performance risk is the likelihood of the recommended instance type not meeting the performance requirement of your workload.

    The lowest performance risk is categorized as 0, and the highest as 5.

    " + }, + "rank":{ + "shape":"Rank", + "documentation":"

    The rank of the Auto Scaling group recommendation option.

    The top recommendation option is ranked as 1.

    " + } + }, + "documentation":"

    Describes a recommendation option for an Auto Scaling group.

    " + }, + "AutoScalingGroupRecommendationOptions":{ + "type":"list", + "member":{"shape":"AutoScalingGroupRecommendationOption"} + }, + "AutoScalingGroupRecommendations":{ + "type":"list", + "member":{"shape":"AutoScalingGroupRecommendation"} + }, + "Code":{"type":"string"}, + "CurrentInstanceType":{"type":"string"}, + "DesiredCapacity":{"type":"integer"}, + "ErrorMessage":{"type":"string"}, + "Filter":{ + "type":"structure", + "members":{ + "name":{ + "shape":"FilterName", + "documentation":"

    The name of the filter.

    Specify Finding to filter the results to a specific findings classification.

    Specify RecommendationSourceType to filter the results to a specific resource type.

    " + }, + "values":{ + "shape":"FilterValues", + "documentation":"

    The value of the filter.

    If you specify the name parameter as Finding, and you're recommendations for an instance, then the valid values are Underprovisioned, Overprovisioned, NotOptimized, or Optimized.

    If you specify the name parameter as Finding, and you're recommendations for an Auto Scaling group, then the valid values are Optimized, or NotOptimized.

    If you specify the name parameter as RecommendationSourceType, then the valid values are EC2Instance, or AutoScalingGroup.

    " + } + }, + "documentation":"

    Describes a filter that returns a more specific list of recommendations.

    " + }, + "FilterName":{ + "type":"string", + "enum":[ + "Finding", + "RecommendationSourceType" + ] + }, + "FilterValue":{"type":"string"}, + "FilterValues":{ + "type":"list", + "member":{"shape":"FilterValue"} + }, + "Filters":{ + "type":"list", + "member":{"shape":"Filter"} + }, + "Finding":{ + "type":"string", + "enum":[ + "Underprovisioned", + "Overprovisioned", + "Optimized", + "NotOptimized" + ] + }, + "GetAutoScalingGroupRecommendationsRequest":{ + "type":"structure", + "members":{ + "accountIds":{ + "shape":"AccountIds", + "documentation":"

    The AWS account IDs for which to return Auto Scaling group recommendations.

    Only one account ID can be specified per request.

    " + }, + "autoScalingGroupArns":{ + "shape":"AutoScalingGroupArns", + "documentation":"

    The Amazon Resource Name (ARN) of the Auto Scaling groups for which to return recommendations.

    " + }, + "nextToken":{ + "shape":"NextToken", + "documentation":"

    The token to advance to the next page of Auto Scaling group recommendations.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of Auto Scaling group recommendations to return with a single call.

    To retrieve the remaining results, make another call with the returned NextToken value.

    " + }, + "filters":{ + "shape":"Filters", + "documentation":"

    An array of objects that describe a filter that returns a more specific list of Auto Scaling group recommendations.

    " + } + } + }, + "GetAutoScalingGroupRecommendationsResponse":{ + "type":"structure", + "members":{ + "nextToken":{ + "shape":"NextToken", + "documentation":"

    The token to use to advance to the next page of Auto Scaling group recommendations.

    This value is null when there are no more pages of Auto Scaling group recommendations to return.

    " + }, + "autoScalingGroupRecommendations":{ + "shape":"AutoScalingGroupRecommendations", + "documentation":"

    An array of objects that describe Auto Scaling group recommendations.

    " + }, + "errors":{ + "shape":"GetRecommendationErrors", + "documentation":"

    An array of objects that describe errors of the request.

    For example, an error is returned if you request recommendations for an unsupported Auto Scaling group.

    " + } + } + }, + "GetEC2InstanceRecommendationsRequest":{ + "type":"structure", + "members":{ + "instanceArns":{ + "shape":"InstanceArns", + "documentation":"

    The Amazon Resource Name (ARN) of the instances for which to return recommendations.

    " + }, + "nextToken":{ + "shape":"NextToken", + "documentation":"

    The token to advance to the next page of instance recommendations.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of instance recommendations to return with a single call.

    To retrieve the remaining results, make another call with the returned NextToken value.

    " + }, + "filters":{ + "shape":"Filters", + "documentation":"

    An array of objects that describe a filter that returns a more specific list of instance recommendations.

    " + }, + "accountIds":{ + "shape":"AccountIds", + "documentation":"

    The AWS account IDs for which to return instance recommendations.

    Only one account ID can be specified per request.

    " + } + } + }, + "GetEC2InstanceRecommendationsResponse":{ + "type":"structure", + "members":{ + "nextToken":{ + "shape":"NextToken", + "documentation":"

    The token to use to advance to the next page of instance recommendations.

    This value is null when there are no more pages of instance recommendations to return.

    " + }, + "instanceRecommendations":{ + "shape":"InstanceRecommendations", + "documentation":"

    An array of objects that describe instance recommendations.

    " + }, + "errors":{ + "shape":"GetRecommendationErrors", + "documentation":"

    An array of objects that describe errors of the request.

    For example, an error is returned if you request recommendations for an instance of an unsupported instance family.

    " + } + } + }, + "GetEC2RecommendationProjectedMetricsRequest":{ + "type":"structure", + "required":[ + "instanceArn", + "stat", + "period", + "startTime", + "endTime" + ], + "members":{ + "instanceArn":{ + "shape":"InstanceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the instances for which to return recommendation projected metrics.

    " + }, + "stat":{ + "shape":"MetricStatistic", + "documentation":"

    The statistic of the projected metrics.

    " + }, + "period":{ + "shape":"Period", + "documentation":"

    The granularity, in seconds, of the projected metrics data points.

    " + }, + "startTime":{ + "shape":"Timestamp", + "documentation":"

    The time stamp of the first projected metrics data point to return.

    " + }, + "endTime":{ + "shape":"Timestamp", + "documentation":"

    The time stamp of the last projected metrics data point to return.

    " + } + } + }, + "GetEC2RecommendationProjectedMetricsResponse":{ + "type":"structure", + "members":{ + "recommendedOptionProjectedMetrics":{ + "shape":"RecommendedOptionProjectedMetrics", + "documentation":"

    An array of objects that describe a projected metrics.

    " + } + } + }, + "GetEnrollmentStatusRequest":{ + "type":"structure", + "members":{ + } + }, + "GetEnrollmentStatusResponse":{ + "type":"structure", + "members":{ + "status":{ + "shape":"Status", + "documentation":"

    The enrollment status of the account.

    " + }, + "statusReason":{ + "shape":"StatusReason", + "documentation":"

    The reason for the enrollment status of the account.

    For example, an account might show a status of Pending because member accounts of an organization require more time to be enrolled in the service.

    " + }, + "memberAccountsEnrolled":{ + "shape":"MemberAccountsEnrolled", + "documentation":"

    Confirms the enrollment status of member accounts within the organization, if the account is a master account of an organization.

    " + } + } + }, + "GetRecommendationError":{ + "type":"structure", + "members":{ + "identifier":{ + "shape":"Identifier", + "documentation":"

    The ID of the error.

    " + }, + "code":{ + "shape":"Code", + "documentation":"

    The error code.

    " + }, + "message":{ + "shape":"Message", + "documentation":"

    The message, or reason, for the error.

    " + } + }, + "documentation":"

    Describes an error experienced when getting recommendations.

    For example, an error is returned if you request recommendations for an unsupported Auto Scaling group, or if you request recommendations for an instance of an unsupported instance family.

    " + }, + "GetRecommendationErrors":{ + "type":"list", + "member":{"shape":"GetRecommendationError"} + }, + "GetRecommendationSummariesRequest":{ + "type":"structure", + "members":{ + "accountIds":{ + "shape":"AccountIds", + "documentation":"

    The AWS account IDs for which to return recommendation summaries.

    Only one account ID can be specified per request.

    " + }, + "nextToken":{ + "shape":"NextToken", + "documentation":"

    The token to advance to the next page of recommendation summaries.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of recommendation summaries to return with a single call.

    To retrieve the remaining results, make another call with the returned NextToken value.

    " + } + } + }, + "GetRecommendationSummariesResponse":{ + "type":"structure", + "members":{ + "nextToken":{ + "shape":"NextToken", + "documentation":"

    The token to use to advance to the next page of recommendation summaries.

    This value is null when there are no more pages of recommendation summaries to return.

    " + }, + "recommendationSummaries":{ + "shape":"RecommendationSummaries", + "documentation":"

    An array of objects that summarize a recommendation.

    " + } + } + }, + "Identifier":{"type":"string"}, + "IncludeMemberAccounts":{"type":"boolean"}, + "InstanceArn":{"type":"string"}, + "InstanceArns":{ + "type":"list", + "member":{"shape":"InstanceArn"} + }, + "InstanceName":{"type":"string"}, + "InstanceRecommendation":{ + "type":"structure", + "members":{ + "instanceArn":{ + "shape":"InstanceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the current instance.

    " + }, + "accountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account ID of the instance recommendation.

    " + }, + "instanceName":{ + "shape":"InstanceName", + "documentation":"

    The name of the current instance.

    " + }, + "currentInstanceType":{ + "shape":"CurrentInstanceType", + "documentation":"

    The instance type of the current instance.

    " + }, + "finding":{ + "shape":"Finding", + "documentation":"

    The finding classification for the instance.

    Findings for instances include:

    • Underprovisioned —An instance is considered under-provisioned when at least one specification of your instance, such as CPU, memory, or network, does not meet the performance requirements of your workload. Under-provisioned instances may lead to poor application performance.

    • Overprovisioned —An instance is considered over-provisioned when at least one specification of your instance, such as CPU, memory, or network, can be sized down while still meeting the performance requirements of your workload, and no specification is under-provisioned. Over-provisioned instances may lead to unnecessary infrastructure cost.

    • Optimized —An instance is considered optimized when all specifications of your instance, such as CPU, memory, and network, meet the performance requirements of your workload and is not over provisioned. An optimized instance runs your workloads with optimal performance and infrastructure cost. For optimized resources, AWS Compute Optimizer might recommend a new generation instance type.

    The values that are returned might be UNDER_PROVISIONED, OVER_PROVISIONED, or OPTIMIZED.

    " + }, + "utilizationMetrics":{ + "shape":"UtilizationMetrics", + "documentation":"

    An array of objects that describe the utilization metrics of the instance.

    " + }, + "lookBackPeriodInDays":{ + "shape":"LookBackPeriodInDays", + "documentation":"

    The number of days for which utilization metrics were analyzed for the instance.

    " + }, + "recommendationOptions":{ + "shape":"RecommendationOptions", + "documentation":"

    An array of objects that describe the recommendation options for the instance.

    " + }, + "recommendationSources":{ + "shape":"RecommendationSources", + "documentation":"

    An array of objects that describe the source resource of the recommendation.

    " + }, + "lastRefreshTimestamp":{ + "shape":"LastRefreshTimestamp", + "documentation":"

    The time stamp of when the instance recommendation was last refreshed.

    " + } + }, + "documentation":"

    Describes an Amazon EC2 instance recommendation.

    " + }, + "InstanceRecommendationOption":{ + "type":"structure", + "members":{ + "instanceType":{ + "shape":"InstanceType", + "documentation":"

    The instance type of the instance recommendation.

    " + }, + "projectedUtilizationMetrics":{ + "shape":"ProjectedUtilizationMetrics", + "documentation":"

    An array of objects that describe the projected utilization metrics of the instance recommendation option.

    " + }, + "performanceRisk":{ + "shape":"PerformanceRisk", + "documentation":"

    The performance risk of the instance recommendation option.

    Performance risk is the likelihood of the recommended instance type not meeting the performance requirement of your workload.

    The lowest performance risk is categorized as 0, and the highest as 5.

    " + }, + "rank":{ + "shape":"Rank", + "documentation":"

    The rank of the instance recommendation option.

    The top recommendation option is ranked as 1.

    " + } + }, + "documentation":"

    Describes a recommendation option for an Amazon EC2 instance.

    " + }, + "InstanceRecommendations":{ + "type":"list", + "member":{"shape":"InstanceRecommendation"} + }, + "InstanceType":{"type":"string"}, + "InternalServerException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request processing has failed because of an unknown error, exception, or failure.

    ", + "exception":true, + "fault":true + }, + "InvalidParameterValueException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    An invalid or out-of-range value was supplied for the input parameter.

    ", + "exception":true, + "synthetic":true + }, + "LastRefreshTimestamp":{"type":"timestamp"}, + "LookBackPeriodInDays":{"type":"double"}, + "MaxResults":{ + "type":"integer", + "box":true + }, + "MaxSize":{"type":"integer"}, + "MemberAccountsEnrolled":{"type":"boolean"}, + "Message":{"type":"string"}, + "MetricName":{ + "type":"string", + "enum":[ + "Cpu", + "Memory" + ] + }, + "MetricStatistic":{ + "type":"string", + "enum":[ + "Maximum", + "Average" + ] + }, + "MetricValue":{"type":"double"}, + "MetricValues":{ + "type":"list", + "member":{"shape":"MetricValue"} + }, + "MinSize":{"type":"integer"}, + "MissingAuthenticationToken":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request must contain either a valid (registered) AWS access key ID or X.509 certificate.

    ", + "exception":true, + "synthetic":true + }, + "NextToken":{"type":"string"}, + "OptInRequiredException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You must opt in to the service to perform this action.

    ", + "exception":true, + "synthetic":true + }, + "PerformanceRisk":{ + "type":"double", + "max":5, + "min":0 + }, + "Period":{"type":"integer"}, + "ProjectedMetric":{ + "type":"structure", + "members":{ + "name":{ + "shape":"MetricName", + "documentation":"

    The name of the projected utilization metric.

    Memory metrics are only returned for resources that have the unified CloudWatch agent installed on them. For more information, see Enabling Memory Utilization with the CloudWatch Agent.

    " + }, + "timestamps":{ + "shape":"Timestamps", + "documentation":"

    The time stamps of the projected utilization metric.

    " + }, + "values":{ + "shape":"MetricValues", + "documentation":"

    The values of the projected utilization metrics.

    " + } + }, + "documentation":"

    Describes a projected utilization metric of a recommendation option, such as an Amazon EC2 instance.

    " + }, + "ProjectedMetrics":{ + "type":"list", + "member":{"shape":"ProjectedMetric"} + }, + "ProjectedUtilizationMetrics":{ + "type":"list", + "member":{"shape":"UtilizationMetric"} + }, + "Rank":{"type":"integer"}, + "RecommendationOptions":{ + "type":"list", + "member":{"shape":"InstanceRecommendationOption"} + }, + "RecommendationSource":{ + "type":"structure", + "members":{ + "recommendationSourceArn":{ + "shape":"RecommendationSourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the recommendation source.

    " + }, + "recommendationSourceType":{ + "shape":"RecommendationSourceType", + "documentation":"

    The resource type of the recommendation source.

    " + } + }, + "documentation":"

    Describes the source of a recommendation, such as an Amazon EC2 instance or Auto Scaling group.

    " + }, + "RecommendationSourceArn":{"type":"string"}, + "RecommendationSourceType":{ + "type":"string", + "enum":[ + "Ec2Instance", + "AutoScalingGroup" + ] + }, + "RecommendationSources":{ + "type":"list", + "member":{"shape":"RecommendationSource"} + }, + "RecommendationSummaries":{ + "type":"list", + "member":{"shape":"RecommendationSummary"} + }, + "RecommendationSummary":{ + "type":"structure", + "members":{ + "summaries":{ + "shape":"Summaries", + "documentation":"

    An array of objects that describe a recommendation summary.

    " + }, + "recommendationResourceType":{ + "shape":"RecommendationSourceType", + "documentation":"

    The resource type of the recommendation.

    " + }, + "accountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account ID of the recommendation summary.

    " + } + }, + "documentation":"

    A summary of a recommendation.

    " + }, + "RecommendedInstanceType":{"type":"string"}, + "RecommendedOptionProjectedMetric":{ + "type":"structure", + "members":{ + "recommendedInstanceType":{ + "shape":"RecommendedInstanceType", + "documentation":"

    The recommended instance type.

    " + }, + "rank":{ + "shape":"Rank", + "documentation":"

    The rank of the recommendation option projected metric.

    The top recommendation option is ranked as 1.

    The projected metric rank correlates to the recommendation option rank. For example, the projected metric ranked as 1 is related to the recommendation option that is also ranked as 1 in the same response.

    " + }, + "projectedMetrics":{ + "shape":"ProjectedMetrics", + "documentation":"

    An array of objects that describe a projected utilization metric.

    " + } + }, + "documentation":"

    Describes a projected utilization metric of a recommendation option.

    " + }, + "RecommendedOptionProjectedMetrics":{ + "type":"list", + "member":{"shape":"RecommendedOptionProjectedMetric"} + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The specified resource was not found.

    ", + "exception":true, + "synthetic":true + }, + "ServiceUnavailableException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request has failed due to a temporary failure of the server.

    ", + "exception":true, + "fault":true + }, + "Status":{ + "type":"string", + "enum":[ + "Active", + "Inactive", + "Pending", + "Failed" + ] + }, + "StatusReason":{"type":"string"}, + "Summaries":{ + "type":"list", + "member":{"shape":"Summary"} + }, + "Summary":{ + "type":"structure", + "members":{ + "name":{ + "shape":"Finding", + "documentation":"

    The finding classification of the recommendation.

    " + }, + "value":{ + "shape":"SummaryValue", + "documentation":"

    The value of the recommendation summary.

    " + } + }, + "documentation":"

    The summary of a recommendation.

    " + }, + "SummaryValue":{"type":"double"}, + "ThrottlingException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The limit on the number of requests per second was exceeded.

    ", + "exception":true, + "synthetic":true + }, + "Timestamp":{"type":"timestamp"}, + "Timestamps":{ + "type":"list", + "member":{"shape":"Timestamp"} + }, + "UpdateEnrollmentStatusRequest":{ + "type":"structure", + "required":["status"], + "members":{ + "status":{ + "shape":"Status", + "documentation":"

    The new enrollment status of the account.

    Accepted options are Active or Inactive. You will get an error if Pending or Failed are specified.

    " + }, + "includeMemberAccounts":{ + "shape":"IncludeMemberAccounts", + "documentation":"

    Indicates whether to enroll member accounts within the organization, if the account is a master account of an organization.

    " + } + } + }, + "UpdateEnrollmentStatusResponse":{ + "type":"structure", + "members":{ + "status":{ + "shape":"Status", + "documentation":"

    The enrollment status of the account.

    " + }, + "statusReason":{ + "shape":"StatusReason", + "documentation":"

    The reason for the enrollment status of the account. For example, an account might show a status of Pending because member accounts of an organization require more time to be enrolled in the service.

    " + } + } + }, + "UtilizationMetric":{ + "type":"structure", + "members":{ + "name":{ + "shape":"MetricName", + "documentation":"

    The name of the utilization metric.

    Memory metrics are only returned for resources that have the unified CloudWatch agent installed on them. For more information, see Enabling Memory Utilization with the CloudWatch Agent.

    " + }, + "statistic":{ + "shape":"MetricStatistic", + "documentation":"

    The statistic of the utilization metric.

    " + }, + "value":{ + "shape":"MetricValue", + "documentation":"

    The value of the utilization metric.

    " + } + }, + "documentation":"

    Describes a utilization metric of a resource, such as an Amazon EC2 instance.

    " + }, + "UtilizationMetrics":{ + "type":"list", + "member":{"shape":"UtilizationMetric"} + } + }, + "documentation":"

    AWS Compute Optimizer is a service that analyzes the configuration and utilization metrics of your AWS resources, such as EC2 instances and Auto Scaling groups. It reports whether your resources are optimal, and generates optimization recommendations to reduce the cost and improve the performance of your workloads. Compute Optimizer also provides recent utilization metric data, as well as projected utilization metric data for the recommendations, which you can use to evaluate which recommendation provides the best price-performance trade-off. The analysis of your usage patterns can help you decide when to move or resize your running resources, and still meet your performance and capacity requirements. For more information about Compute Optimizer, see the AWS Compute Optimizer User Guide.

    " +} diff --git a/services/config/build.properties b/services/config/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/config/build.properties +++ b/services/config/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/config/pom.xml b/services/config/pom.xml index d6586984f2a1..3ede1cf0da4d 100644 --- a/services/config/pom.xml +++ b/services/config/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + connectparticipant + AWS Java SDK :: Services :: ConnectParticipant + The AWS Java SDK for ConnectParticipant module holds the client classes that are used for + communicating with ConnectParticipant. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.connectparticipant + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/connectparticipant/src/main/resources/codegen-resources/paginators-1.json b/services/connectparticipant/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..d37a1a26126f --- /dev/null +++ b/services/connectparticipant/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "GetTranscript": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/connectparticipant/src/main/resources/codegen-resources/service-2.json b/services/connectparticipant/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..50fff6ee9cae --- /dev/null +++ b/services/connectparticipant/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,533 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2018-09-07", + "endpointPrefix":"participant.connect", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"Amazon Connect Participant", + "serviceFullName":"Amazon Connect Participant Service", + "serviceId":"ConnectParticipant", + "signatureVersion":"v4", + "signingName":"execute-api", + "uid":"connectparticipant-2018-09-07" + }, + "operations":{ + "CreateParticipantConnection":{ + "name":"CreateParticipantConnection", + "http":{ + "method":"POST", + "requestUri":"/participant/connection" + }, + "input":{"shape":"CreateParticipantConnectionRequest"}, + "output":{"shape":"CreateParticipantConnectionResponse"}, + "errors":[ + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Creates the participant's connection. Note that ParticipantToken is used for invoking this API instead of ConnectionToken.

    The participant token is valid for the lifetime of the participant – until the they are part of a contact.

    The response URL for WEBSOCKET Type has a connect expiry timeout of 100s. Clients must manually connect to the returned websocket URL and subscribe to the desired topic.

    For chat, you need to publish the following on the established websocket connection:

    {\"topic\":\"aws/subscribe\",\"content\":{\"topics\":[\"aws/chat\"]}}

    Upon websocket URL expiry, as specified in the response ConnectionExpiry parameter, clients need to call this API again to obtain a new websocket URL and perform the same steps as before.

    " + }, + "DisconnectParticipant":{ + "name":"DisconnectParticipant", + "http":{ + "method":"POST", + "requestUri":"/participant/disconnect" + }, + "input":{"shape":"DisconnectParticipantRequest"}, + "output":{"shape":"DisconnectParticipantResponse"}, + "errors":[ + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Disconnects a participant. Note that ConnectionToken is used for invoking this API instead of ParticipantToken.

    " + }, + "GetTranscript":{ + "name":"GetTranscript", + "http":{ + "method":"POST", + "requestUri":"/participant/transcript" + }, + "input":{"shape":"GetTranscriptRequest"}, + "output":{"shape":"GetTranscriptResponse"}, + "errors":[ + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Retrieves a transcript of the session. Note that ConnectionToken is used for invoking this API instead of ParticipantToken.

    " + }, + "SendEvent":{ + "name":"SendEvent", + "http":{ + "method":"POST", + "requestUri":"/participant/event" + }, + "input":{"shape":"SendEventRequest"}, + "output":{"shape":"SendEventResponse"}, + "errors":[ + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Sends an event. Note that ConnectionToken is used for invoking this API instead of ParticipantToken.

    " + }, + "SendMessage":{ + "name":"SendMessage", + "http":{ + "method":"POST", + "requestUri":"/participant/message" + }, + "input":{"shape":"SendMessageRequest"}, + "output":{"shape":"SendMessageResponse"}, + "errors":[ + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Sends a message. Note that ConnectionToken is used for invoking this API instead of ParticipantToken.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "error":{"httpStatusCode":403}, + "exception":true + }, + "ChatContent":{ + "type":"string", + "max":1024, + "min":1 + }, + "ChatContentType":{ + "type":"string", + "max":100, + "min":1 + }, + "ChatItemId":{ + "type":"string", + "max":256, + "min":1 + }, + "ChatItemType":{ + "type":"string", + "enum":[ + "MESSAGE", + "EVENT", + "CONNECTION_ACK" + ] + }, + "ClientToken":{ + "type":"string", + "max":500 + }, + "ConnectionCredentials":{ + "type":"structure", + "members":{ + "ConnectionToken":{ + "shape":"ParticipantToken", + "documentation":"

    The connection token.

    " + }, + "Expiry":{ + "shape":"ISO8601Datetime", + "documentation":"

    The expiration of the token.

    It's specified in ISO 8601 format: yyyy-MM-ddThh:mm:ss.SSSZ. For example, 2019-11-08T02:41:28.172Z.

    " + } + }, + "documentation":"

    Connection credentials.

    " + }, + "ConnectionType":{ + "type":"string", + "enum":[ + "WEBSOCKET", + "CONNECTION_CREDENTIALS" + ] + }, + "ConnectionTypeList":{ + "type":"list", + "member":{"shape":"ConnectionType"}, + "min":1 + }, + "ContactId":{ + "type":"string", + "max":256, + "min":1 + }, + "CreateParticipantConnectionRequest":{ + "type":"structure", + "required":[ + "Type", + "ParticipantToken" + ], + "members":{ + "Type":{ + "shape":"ConnectionTypeList", + "documentation":"

    Type of connection information required.

    " + }, + "ParticipantToken":{ + "shape":"ParticipantToken", + "documentation":"

    Participant Token as obtained from StartChatContact API response.

    ", + "location":"header", + "locationName":"X-Amz-Bearer" + } + } + }, + "CreateParticipantConnectionResponse":{ + "type":"structure", + "members":{ + "Websocket":{ + "shape":"Websocket", + "documentation":"

    Creates the participant's websocket connection.

    " + }, + "ConnectionCredentials":{ + "shape":"ConnectionCredentials", + "documentation":"

    Creates the participant's connection credentials. The authentication token associated with the participant's connection.

    " + } + } + }, + "DisconnectParticipantRequest":{ + "type":"structure", + "required":["ConnectionToken"], + "members":{ + "ClientToken":{ + "shape":"ClientToken", + "documentation":"

    A unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

    ", + "idempotencyToken":true + }, + "ConnectionToken":{ + "shape":"ParticipantToken", + "documentation":"

    The authentication token associated with the participant's connection.

    ", + "location":"header", + "locationName":"X-Amz-Bearer" + } + } + }, + "DisconnectParticipantResponse":{ + "type":"structure", + "members":{ + } + }, + "DisplayName":{ + "type":"string", + "max":256, + "min":1 + }, + "GetTranscriptRequest":{ + "type":"structure", + "required":["ConnectionToken"], + "members":{ + "ContactId":{ + "shape":"ContactId", + "documentation":"

    The contactId from the current contact chain for which transcript is needed.

    " + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return in the page. Default: 10.

    ", + "box":true + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The pagination token. Use the value returned previously in the next subsequent request to retrieve the next set of results.

    " + }, + "ScanDirection":{ + "shape":"ScanDirection", + "documentation":"

    The direction from StartPosition from which to retrieve message. Default: BACKWARD when no StartPosition is provided, FORWARD with StartPosition.

    " + }, + "SortOrder":{ + "shape":"SortKey", + "documentation":"

    The sort order for the records. Default: DESCENDING.

    " + }, + "StartPosition":{ + "shape":"StartPosition", + "documentation":"

    A filtering option for where to start.

    " + }, + "ConnectionToken":{ + "shape":"ParticipantToken", + "documentation":"

    The authentication token associated with the participant's connection.

    ", + "location":"header", + "locationName":"X-Amz-Bearer" + } + } + }, + "GetTranscriptResponse":{ + "type":"structure", + "members":{ + "InitialContactId":{ + "shape":"ContactId", + "documentation":"

    The initial contact ID for the contact.

    " + }, + "Transcript":{ + "shape":"Transcript", + "documentation":"

    The list of messages in the session.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The pagination token. Use the value returned previously in the next subsequent request to retrieve the next set of results.

    " + } + } + }, + "ISO8601Datetime":{"type":"string"}, + "Instant":{ + "type":"string", + "max":100, + "min":1 + }, + "InternalServerException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    This exception occurs when there is an internal failure in the Amazon Connect service.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "Item":{ + "type":"structure", + "members":{ + "AbsoluteTime":{ + "shape":"Instant", + "documentation":"

    The time when the message or event was sent.

    It's specified in ISO 8601 format: yyyy-MM-ddThh:mm:ss.SSSZ. For example, 2019-11-08T02:41:28.172Z.

    " + }, + "Content":{ + "shape":"ChatContent", + "documentation":"

    The content of the message or event.

    " + }, + "ContentType":{ + "shape":"ChatContentType", + "documentation":"

    The type of content of the item.

    " + }, + "Id":{ + "shape":"ChatItemId", + "documentation":"

    The ID of the item.

    " + }, + "Type":{ + "shape":"ChatItemType", + "documentation":"

    Type of the item: message or event.

    " + }, + "ParticipantId":{ + "shape":"ParticipantId", + "documentation":"

    The ID of the sender in the session.

    " + }, + "DisplayName":{ + "shape":"DisplayName", + "documentation":"

    The chat display name of the sender.

    " + }, + "ParticipantRole":{ + "shape":"ParticipantRole", + "documentation":"

    The role of the sender. For example, is it a customer, agent, or system.

    " + } + }, + "documentation":"

    An item - message or event - that has been sent.

    " + }, + "MaxResults":{ + "type":"integer", + "max":100, + "min":0 + }, + "Message":{"type":"string"}, + "MostRecent":{ + "type":"integer", + "max":100, + "min":0 + }, + "NextToken":{ + "type":"string", + "max":1000, + "min":1 + }, + "ParticipantId":{ + "type":"string", + "max":256, + "min":1 + }, + "ParticipantRole":{ + "type":"string", + "enum":[ + "AGENT", + "CUSTOMER", + "SYSTEM" + ] + }, + "ParticipantToken":{ + "type":"string", + "max":1000, + "min":1 + }, + "PreSignedConnectionUrl":{ + "type":"string", + "max":2000, + "min":1 + }, + "Reason":{ + "type":"string", + "max":2000, + "min":1 + }, + "ScanDirection":{ + "type":"string", + "enum":[ + "FORWARD", + "BACKWARD" + ] + }, + "SendEventRequest":{ + "type":"structure", + "required":[ + "ContentType", + "ConnectionToken" + ], + "members":{ + "ContentType":{ + "shape":"ChatContentType", + "documentation":"

    The content type of the request. Supported types are:

    • application/vnd.amazonaws.connect.event.typing

    • application/vnd.amazonaws.connect.event.connection.acknowledged

    " + }, + "Content":{ + "shape":"ChatContent", + "documentation":"

    The content of the event to be sent (for example, message text). This is not yet supported.

    " + }, + "ClientToken":{ + "shape":"ClientToken", + "documentation":"

    A unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

    ", + "idempotencyToken":true + }, + "ConnectionToken":{ + "shape":"ParticipantToken", + "documentation":"

    The authentication token associated with the participant's connection.

    ", + "location":"header", + "locationName":"X-Amz-Bearer" + } + } + }, + "SendEventResponse":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"ChatItemId", + "documentation":"

    The ID of the response.

    " + }, + "AbsoluteTime":{ + "shape":"Instant", + "documentation":"

    The time when the event was sent.

    It's specified in ISO 8601 format: yyyy-MM-ddThh:mm:ss.SSSZ. For example, 2019-11-08T02:41:28.172Z.

    " + } + } + }, + "SendMessageRequest":{ + "type":"structure", + "required":[ + "ContentType", + "Content", + "ConnectionToken" + ], + "members":{ + "ContentType":{ + "shape":"ChatContentType", + "documentation":"

    The type of the content. Supported types are text/plain.

    " + }, + "Content":{ + "shape":"ChatContent", + "documentation":"

    The content of the message.

    " + }, + "ClientToken":{ + "shape":"ClientToken", + "documentation":"

    A unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

    ", + "idempotencyToken":true + }, + "ConnectionToken":{ + "shape":"ParticipantToken", + "documentation":"

    The authentication token associated with the connection.

    ", + "location":"header", + "locationName":"X-Amz-Bearer" + } + } + }, + "SendMessageResponse":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"ChatItemId", + "documentation":"

    The ID of the message.

    " + }, + "AbsoluteTime":{ + "shape":"Instant", + "documentation":"

    The time when the message was sent.

    It's specified in ISO 8601 format: yyyy-MM-ddThh:mm:ss.SSSZ. For example, 2019-11-08T02:41:28.172Z.

    " + } + } + }, + "SortKey":{ + "type":"string", + "enum":[ + "DESCENDING", + "ASCENDING" + ] + }, + "StartPosition":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"ChatItemId", + "documentation":"

    The ID of the message or event where to start.

    " + }, + "AbsoluteTime":{ + "shape":"Instant", + "documentation":"

    The time in ISO format where to start.

    It's specified in ISO 8601 format: yyyy-MM-ddThh:mm:ss.SSSZ. For example, 2019-11-08T02:41:28.172Z.

    " + }, + "MostRecent":{ + "shape":"MostRecent", + "documentation":"

    The start position of the most recent message where you want to start.

    " + } + }, + "documentation":"

    A filtering option for where to start. For example, if you sent 100 messages, start with message 50.

    " + }, + "ThrottlingException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"Message"} + }, + "documentation":"

    The request was denied due to request throttling.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "Transcript":{ + "type":"list", + "member":{"shape":"Item"} + }, + "ValidationException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"Reason"} + }, + "documentation":"

    The input fails to satisfy the constraints specified by Amazon Connect.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "Websocket":{ + "type":"structure", + "members":{ + "Url":{ + "shape":"PreSignedConnectionUrl", + "documentation":"

    The URL of the websocket.

    " + }, + "ConnectionExpiry":{ + "shape":"ISO8601Datetime", + "documentation":"

    The URL expiration timestamp in ISO date format.

    It's specified in ISO 8601 format: yyyy-MM-ddThh:mm:ss.SSSZ. For example, 2019-11-08T02:41:28.172Z.

    " + } + }, + "documentation":"

    The websocket for the participant's connection.

    " + } + }, + "documentation":"

    Amazon Connect is a cloud-based contact center solution that makes it easy to set up and manage a customer contact center and provide reliable customer engagement at any scale.

    Amazon Connect enables customer contacts through voice or chat.

    The APIs described here are used by chat participants, such as agents and customers.

    " +} diff --git a/services/costandusagereport/build.properties b/services/costandusagereport/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/costandusagereport/build.properties +++ b/services/costandusagereport/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/costandusagereport/pom.xml b/services/costandusagereport/pom.xml index 483fad7c95f1..fcba43f2240b 100644 --- a/services/costandusagereport/pom.xml +++ b/services/costandusagereport/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + dataexchange + AWS Java SDK :: Services :: DataExchange + The AWS Java SDK for DataExchange module holds the client classes that are used for + communicating with DataExchange. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.dataexchange + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/dataexchange/src/main/resources/codegen-resources/paginators-1.json b/services/dataexchange/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..d76ccf7f38be --- /dev/null +++ b/services/dataexchange/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,28 @@ +{ + "pagination": { + "ListDataSetRevisions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Revisions" + }, + "ListDataSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "DataSets" + }, + "ListJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Jobs" + }, + "ListRevisionAssets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Assets" + } + } +} \ No newline at end of file diff --git a/services/dataexchange/src/main/resources/codegen-resources/service-2.json b/services/dataexchange/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..8057f64d11f5 --- /dev/null +++ b/services/dataexchange/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,2696 @@ +{ + "metadata": { + "apiVersion": "2017-07-25", + "endpointPrefix": "dataexchange", + "signingName": "dataexchange", + "serviceFullName": "AWS Data Exchange", + "serviceId": "DataExchange", + "protocol": "rest-json", + "jsonVersion": "1.1", + "uid": "dataexchange-2017-07-25", + "signatureVersion": "v4" + }, + "operations": { + "CancelJob": { + "name": "CancelJob", + "http": { + "method": "DELETE", + "requestUri": "/v1/jobs/{JobId}", + "responseCode": 204 + }, + "input": { + "shape": "CancelJobRequest" + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation cancels a job. Jobs can be cancelled only when they are in the WAITING state.

    " + }, + "CreateDataSet": { + "name": "CreateDataSet", + "http": { + "method": "POST", + "requestUri": "/v1/data-sets", + "responseCode": 201 + }, + "input": { + "shape": "CreateDataSetRequest" + }, + "output": { + "shape": "CreateDataSetResponse", + "documentation": "

    201 response

    " + }, + "errors": [ + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "ServiceLimitExceededException", + "documentation": "

    402 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + } + ], + "documentation": "

    This operation creates a data set.

    " + }, + "CreateJob": { + "name": "CreateJob", + "http": { + "method": "POST", + "requestUri": "/v1/jobs", + "responseCode": 201 + }, + "input": { + "shape": "CreateJobRequest" + }, + "output": { + "shape": "CreateJobResponse", + "documentation": "

    201 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + } + ], + "documentation": "

    This operation creates a job.

    " + }, + "CreateRevision": { + "name": "CreateRevision", + "http": { + "method": "POST", + "requestUri": "/v1/data-sets/{DataSetId}/revisions", + "responseCode": 201 + }, + "input": { + "shape": "CreateRevisionRequest" + }, + "output": { + "shape": "CreateRevisionResponse", + "documentation": "

    201 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + } + ], + "documentation": "

    This operation creates a revision for a data set.

    " + }, + "DeleteAsset": { + "name": "DeleteAsset", + "http": { + "method": "DELETE", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}/assets/{AssetId}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteAssetRequest" + }, + "errors": [ + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + }, + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation deletes an asset.

    " + }, + "DeleteDataSet": { + "name": "DeleteDataSet", + "http": { + "method": "DELETE", + "requestUri": "/v1/data-sets/{DataSetId}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteDataSetRequest" + }, + "errors": [ + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + }, + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation deletes a data set.

    " + }, + "DeleteRevision": { + "name": "DeleteRevision", + "http": { + "method": "DELETE", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteRevisionRequest" + }, + "errors": [ + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + }, + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation deletes a revision.

    " + }, + "GetAsset": { + "name": "GetAsset", + "http": { + "method": "GET", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}/assets/{AssetId}", + "responseCode": 200 + }, + "input": { + "shape": "GetAssetRequest" + }, + "output": { + "shape": "GetAssetResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation returns information about an asset.

    " + }, + "GetDataSet": { + "name": "GetDataSet", + "http": { + "method": "GET", + "requestUri": "/v1/data-sets/{DataSetId}", + "responseCode": 200 + }, + "input": { + "shape": "GetDataSetRequest" + }, + "output": { + "shape": "GetDataSetResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation returns information about a data set.

    " + }, + "GetJob": { + "name": "GetJob", + "http": { + "method": "GET", + "requestUri": "/v1/jobs/{JobId}", + "responseCode": 200 + }, + "input": { + "shape": "GetJobRequest" + }, + "output": { + "shape": "GetJobResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation returns information about a job.

    " + }, + "GetRevision": { + "name": "GetRevision", + "http": { + "method": "GET", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}", + "responseCode": 200 + }, + "input": { + "shape": "GetRevisionRequest" + }, + "output": { + "shape": "GetRevisionResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation returns information about a revision.

    " + }, + "ListDataSetRevisions": { + "name": "ListDataSetRevisions", + "http": { + "method": "GET", + "requestUri": "/v1/data-sets/{DataSetId}/revisions", + "responseCode": 200 + }, + "input": { + "shape": "ListDataSetRevisionsRequest" + }, + "output": { + "shape": "ListDataSetRevisionsResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation lists a data set's revisions sorted by CreatedAt in descending order.

    " + }, + "ListDataSets": { + "name": "ListDataSets", + "http": { + "method": "GET", + "requestUri": "/v1/data-sets", + "responseCode": 200 + }, + "input": { + "shape": "ListDataSetsRequest" + }, + "output": { + "shape": "ListDataSetsResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation lists your data sets. When listing by origin OWNED, results are sorted by CreatedAt in descending order. When listing by origin ENTITLED, there is no order and the maxResults parameter is ignored.

    " + }, + "ListJobs": { + "name": "ListJobs", + "http": { + "method": "GET", + "requestUri": "/v1/jobs", + "responseCode": 200 + }, + "input": { + "shape": "ListJobsRequest" + }, + "output": { + "shape": "ListJobsResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation lists your jobs sorted by CreatedAt in descending order.

    " + }, + "ListRevisionAssets": { + "name": "ListRevisionAssets", + "http": { + "method": "GET", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}/assets", + "responseCode": 200 + }, + "input": { + "shape": "ListRevisionAssetsRequest" + }, + "output": { + "shape": "ListRevisionAssetsResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + } + ], + "documentation": "

    This operation lists a revision's assets sorted alphabetically in descending order.

    " + }, + "ListTagsForResource": { + "name": "ListTagsForResource", + "http": { + "method": "GET", + "requestUri": "/tags/{resource-arn}", + "responseCode": 200 + }, + "input": { + "shape": "ListTagsForResourceRequest" + }, + "output": { + "shape": "ListTagsForResourceResponse", + "documentation": "

    200 response

    " + }, + "errors": [], + "documentation": "

    This operation lists the tags on the resource.

    " + }, + "StartJob": { + "name": "StartJob", + "http": { + "method": "PATCH", + "requestUri": "/v1/jobs/{JobId}", + "responseCode": 202 + }, + "input": { + "shape": "StartJobRequest" + }, + "output": { + "shape": "StartJobResponse", + "documentation": "

    202 response

    " + }, + "errors": [ + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + }, + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation starts a job.

    " + }, + "TagResource": { + "name": "TagResource", + "http": { + "method": "POST", + "requestUri": "/tags/{resource-arn}", + "responseCode": 204 + }, + "input": { + "shape": "TagResourceRequest" + }, + "errors": [], + "documentation": "

    This operation tags a resource.

    " + }, + "UntagResource": { + "name": "UntagResource", + "http": { + "method": "DELETE", + "requestUri": "/tags/{resource-arn}", + "responseCode": 204 + }, + "input": { + "shape": "UntagResourceRequest" + }, + "errors": [], + "documentation": "

    This operation removes one or more tags from a resource.

    " + }, + "UpdateAsset": { + "name": "UpdateAsset", + "http": { + "method": "PATCH", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}/assets/{AssetId}", + "responseCode": 200 + }, + "input": { + "shape": "UpdateAssetRequest" + }, + "output": { + "shape": "UpdateAssetResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + }, + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation updates an asset.

    " + }, + "UpdateDataSet": { + "name": "UpdateDataSet", + "http": { + "method": "PATCH", + "requestUri": "/v1/data-sets/{DataSetId}", + "responseCode": 200 + }, + "input": { + "shape": "UpdateDataSetRequest" + }, + "output": { + "shape": "UpdateDataSetResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + } + ], + "documentation": "

    This operation updates a data set.

    " + }, + "UpdateRevision": { + "name": "UpdateRevision", + "http": { + "method": "PATCH", + "requestUri": "/v1/data-sets/{DataSetId}/revisions/{RevisionId}", + "responseCode": 200 + }, + "input": { + "shape": "UpdateRevisionRequest" + }, + "output": { + "shape": "UpdateRevisionResponse", + "documentation": "

    200 response

    " + }, + "errors": [ + { + "shape": "ValidationException", + "documentation": "

    400 response

    " + }, + { + "shape": "InternalServerException", + "documentation": "

    500 response

    " + }, + { + "shape": "AccessDeniedException", + "documentation": "

    403 response

    " + }, + { + "shape": "ResourceNotFoundException", + "documentation": "

    404 response

    " + }, + { + "shape": "ThrottlingException", + "documentation": "

    429 response

    " + }, + { + "shape": "ConflictException", + "documentation": "

    409 response

    " + } + ], + "documentation": "

    This operation updates a revision.

    " + } + }, + "shapes": { + "AccessDeniedException": { + "type": "structure", + "members": { + "Message": { + "shape": "__string", + "documentation": "

    Access to the resource is denied.

    " + } + }, + "documentation": "

    Access to the resource is denied.

    ", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 403 + } + }, + "Arn": { + "type": "string", + "documentation": "

    An Amazon Resource Name (ARN) that uniquely identifies an AWS resource.

    " + }, + "AssetDestinationEntry": { + "type": "structure", + "members": { + "AssetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the asset.

    " + }, + "Bucket": { + "shape": "__string", + "documentation": "

    The S3 bucket that is the destination for the asset.

    " + }, + "Key": { + "shape": "__string", + "documentation": "

    The name of the object in Amazon S3 for the asset.

    " + } + }, + "documentation": "

    The destination for the asset.

    ", + "required": [ + "Bucket", + "AssetId" + ] + }, + "AssetDetails": { + "type": "structure", + "members": { + "S3SnapshotAsset": { + "shape": "S3SnapshotAsset" + } + } + }, + "AssetEntry": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the asset.

    " + }, + "AssetDetails": { + "shape": "AssetDetails", + "documentation": "

    Information about the asset, including its size.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the asset was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this asset.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the asset.

    " + }, + "Name": { + "shape": "AssetName", + "documentation": "

    The name of the asset. When importing from Amazon S3, the S3 object key is used as the asset name. When exporting to Amazon S3, the asset name is used as default target S3 object key.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this asset.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The asset ID of the owned asset corresponding to the entitled asset being viewed. This parameter is returned when an asset owner is viewing the entitled copy of its owned asset.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the asset was last updated, in ISO 8601 format.

    " + } + }, + "documentation": "

    An asset in AWS Data Exchange is a piece of data that can be stored as an S3 object. The asset can be a structured data file, an image file, or some other data file. When you create an import job for your files, you create an asset in AWS Data Exchange for each of those files.

    ", + "required": [ + "AssetType", + "CreatedAt", + "DataSetId", + "Id", + "Arn", + "AssetDetails", + "UpdatedAt", + "RevisionId", + "Name" + ] + }, + "AssetName": { + "type": "string", + "documentation": "

    The name of the asset. When importing from Amazon S3, the S3 object key is used as the asset name. When exporting to Amazon S3, the asset name is used as default target S3 object key.

    " + }, + "AssetSourceEntry": { + "type": "structure", + "members": { + "Bucket": { + "shape": "__string", + "documentation": "

    The S3 bucket that's part of the source of the asset.

    " + }, + "Key": { + "shape": "__string", + "documentation": "

    The name of the object in Amazon S3 for the asset.

    " + } + }, + "documentation": "

    The source of the assets.

    ", + "required": [ + "Bucket", + "Key" + ] + }, + "AssetType": { + "type": "string", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    ", + "enum": [ + "S3_SNAPSHOT" + ] + }, + "CancelJobRequest": { + "type": "structure", + "members": { + "JobId": { + "shape": "__string", + "location": "uri", + "locationName": "JobId", + "documentation": "

    The unique identifier for a job.

    " + } + }, + "required": [ + "JobId" + ] + }, + "Code": { + "type": "string", + "enum": [ + "ACCESS_DENIED_EXCEPTION", + "INTERNAL_SERVER_EXCEPTION", + "MALWARE_DETECTED", + "RESOURCE_NOT_FOUND_EXCEPTION", + "SERVICE_QUOTA_EXCEEDED_EXCEPTION", + "VALIDATION_EXCEPTION", + "MALWARE_SCAN_ENCRYPTED_FILE" + ] + }, + "ConflictException": { + "type": "structure", + "members": { + "Message": { + "shape": "__string", + "documentation": "

    The request couldn't be completed because it conflicted with the current state of the resource.

    " + }, + "ResourceId": { + "shape": "__string", + "documentation": "

    The unique identifier for the resource with the conflict.

    " + }, + "ResourceType": { + "shape": "ResourceType", + "documentation": "

    The type of the resource with the conflict.

    " + } + }, + "documentation": "

    The request couldn't be completed because it conflicted with the current state of the resource.

    ", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 409 + } + }, + "CreateDataSetRequest": { + "type": "structure", + "members": { + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "Description": { + "shape": "Description", + "documentation": "

    A description for the data set. This value can be up to 16,348 characters long.

    " + }, + "Name": { + "shape": "Name", + "documentation": "

    The name of the data set.

    " + }, + "Tags": { + "shape": "MapOf__string", + "documentation": "

    A data set tag is an optional label that you can assign to a data set when you create it. Each tag consists of a key and an optional value, both of which you define. When you use tagging, you can also use tag-based access control in IAM policies to control access to these data sets and revisions.

    " + } + }, + "documentation": "

    The request body for CreateDataSet.

    ", + "required": [ + "AssetType", + "Description", + "Name" + ] + }, + "CreateDataSetResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the data set.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was created, in ISO 8601 format.

    " + }, + "Description": { + "shape": "Description", + "documentation": "

    The description for the data set.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set.

    " + }, + "Name": { + "shape": "Name", + "documentation": "

    The name of the data set.

    " + }, + "Origin": { + "shape": "Origin", + "documentation": "

    A property that defines the data set as OWNED by the account (for providers) or ENTITLED to the account (for subscribers).

    " + }, + "OriginDetails": { + "shape": "OriginDetails", + "documentation": "

    If the origin of this data set is ENTITLED, includes the details for the product on AWS Marketplace.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The data set ID of the owned data set corresponding to the entitled data set being viewed. This parameter is returned when a data set owner is viewing the entitled copy of its owned data set.

    " + }, + "Tags": { + "shape": "MapOf__string", + "documentation": "

    The tags for the data set.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was last updated, in ISO 8601 format.

    " + } + } + }, + "CreateJobRequest": { + "type": "structure", + "members": { + "Details": { + "shape": "RequestDetails", + "documentation": "

    The details for the CreateJob request.

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The type of job to be created.

    " + } + }, + "documentation": "

    The request body for CreateJob.

    ", + "required": [ + "Type", + "Details" + ] + }, + "CreateJobResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the job.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the job was created, in ISO 8601 format.

    " + }, + "Details": { + "shape": "ResponseDetails", + "documentation": "

    Details about the job.

    " + }, + "Errors": { + "shape": "ListOfJobError", + "documentation": "

    The errors associated with jobs.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the job.

    " + }, + "State": { + "shape": "State", + "documentation": "

    The state of the job.

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The job type.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the job was last updated, in ISO 8601 format.

    " + } + } + }, + "CreateRevisionRequest": { + "type": "structure", + "members": { + "Comment": { + "shape": "__stringMin0Max16384", + "documentation": "

    An optional comment about the revision.

    " + }, + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "Tags": { + "shape": "MapOf__string", + "documentation": "

    A revision tag is an optional label that you can assign to a revision when you create it. Each tag consists of a key and an optional value, both of which you define. When you use tagging, you can also use tag-based access control in IAM policies to control access to these data sets and revisions.

    " + } + }, + "documentation": "

    The request body for CreateRevision.

    ", + "required": [ + "DataSetId" + ] + }, + "CreateRevisionResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the revision

    " + }, + "Comment": { + "shape": "__stringMin0Max16384", + "documentation": "

    An optional comment about the revision.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this revision.

    " + }, + "Finalized": { + "shape": "__boolean", + "documentation": "

    To publish a revision to a data set in a product, the revision must first be finalized. Finalizing a revision tells AWS Data Exchange that your changes to the assets in the revision are complete. After it's in this read-only state, you can publish the revision to your products.

    Finalized revisions can be published through the AWS Data Exchange console or the AWS Marketplace Catalog API, using the StartChangeSet AWS Marketplace Catalog API action. When using the API, revisions are uniquely identified by their ARN.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The revision ID of the owned revision corresponding to the entitled revision being viewed. This parameter is returned when a revision owner is viewing the entitled copy of its owned revision.

    " + }, + "Tags": { + "shape": "MapOf__string", + "documentation": "

    The tags for the revision.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was last updated, in ISO 8601 format.

    " + } + } + }, + "DataSetEntry": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the data set.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was created, in ISO 8601 format.

    " + }, + "Description": { + "shape": "Description", + "documentation": "

    The description for the data set.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set.

    " + }, + "Name": { + "shape": "Name", + "documentation": "

    The name of the data set.

    " + }, + "Origin": { + "shape": "Origin", + "documentation": "

    A property that defines the data set as OWNED by the account (for providers) or ENTITLED to the account (for subscribers).

    " + }, + "OriginDetails": { + "shape": "OriginDetails", + "documentation": "

    If the origin of this data set is ENTITLED, includes the details for the product on AWS Marketplace.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The data set ID of the owned data set corresponding to the entitled data set being viewed. This parameter is returned when a data set owner is viewing the entitled copy of its owned data set.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was last updated, in ISO 8601 format.

    " + } + }, + "documentation": "

    A data set is an AWS resource with one or more revisions.

    ", + "required": [ + "Origin", + "AssetType", + "Description", + "CreatedAt", + "Id", + "Arn", + "UpdatedAt", + "Name" + ] + }, + "DeleteAssetRequest": { + "type": "structure", + "members": { + "AssetId": { + "shape": "__string", + "location": "uri", + "locationName": "AssetId", + "documentation": "

    The unique identifier for an asset.

    " + }, + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "required": [ + "RevisionId", + "AssetId", + "DataSetId" + ] + }, + "DeleteDataSetRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + } + }, + "required": [ + "DataSetId" + ] + }, + "DeleteRevisionRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "required": [ + "RevisionId", + "DataSetId" + ] + }, + "Description": { + "type": "string", + "documentation": "

    A description of a resource.

    " + }, + "Details": { + "type": "structure", + "members": { + "ImportAssetFromSignedUrlJobErrorDetails": { + "shape": "ImportAssetFromSignedUrlJobErrorDetails" + }, + "ImportAssetsFromS3JobErrorDetails": { + "shape": "ListOfAssetSourceEntry" + } + } + }, + "ExportAssetToSignedUrlRequestDetails": { + "type": "structure", + "members": { + "AssetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the asset that is exported to a signed URL.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this export job.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this export request.

    " + } + }, + "documentation": "

    Details of the operation to be performed by the job.

    ", + "required": [ + "DataSetId", + "AssetId", + "RevisionId" + ] + }, + "ExportAssetToSignedUrlResponseDetails": { + "type": "structure", + "members": { + "AssetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the asset associated with this export job.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this export job.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this export response.

    " + }, + "SignedUrl": { + "shape": "__string", + "documentation": "

    The signed URL for the export request.

    " + }, + "SignedUrlExpiresAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the signed URL expires, in ISO 8601 format.

    " + } + }, + "documentation": "

    The details of the export to signed URL response.

    ", + "required": [ + "DataSetId", + "AssetId", + "RevisionId" + ] + }, + "ExportAssetsToS3RequestDetails": { + "type": "structure", + "members": { + "AssetDestinations": { + "shape": "ListOfAssetDestinationEntry", + "documentation": "

    The destination for the asset.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this export job.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this export request.

    " + } + }, + "documentation": "

    Details of the operation to be performed by the job.

    ", + "required": [ + "AssetDestinations", + "DataSetId", + "RevisionId" + ] + }, + "ExportAssetsToS3ResponseDetails": { + "type": "structure", + "members": { + "AssetDestinations": { + "shape": "ListOfAssetDestinationEntry", + "documentation": "

    The destination in Amazon S3 where the asset is exported.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this export job.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this export response.

    " + } + }, + "documentation": "

    Details about the export to Amazon S3 response.

    ", + "required": [ + "AssetDestinations", + "DataSetId", + "RevisionId" + ] + }, + "GetAssetRequest": { + "type": "structure", + "members": { + "AssetId": { + "shape": "__string", + "location": "uri", + "locationName": "AssetId", + "documentation": "

    The unique identifier for an asset.

    " + }, + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "required": [ + "RevisionId", + "AssetId", + "DataSetId" + ] + }, + "GetAssetResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the asset.

    " + }, + "AssetDetails": { + "shape": "AssetDetails", + "documentation": "

    Information about the asset, including its size.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the asset was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this asset.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the asset.

    " + }, + "Name": { + "shape": "AssetName", + "documentation": "

    The name of the asset When importing from Amazon S3, the S3 object key is used as the asset name. When exporting to Amazon S3, the asset name is used as default target S3 object key.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this asset.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The asset ID of the owned asset corresponding to the entitled asset being viewed. This parameter is returned when an asset owner is viewing the entitled copy of its owned asset.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the asset was last updated, in ISO 8601 format.

    " + } + } + }, + "GetDataSetRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + } + }, + "required": [ + "DataSetId" + ] + }, + "GetDataSetResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the data set.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was created, in ISO 8601 format.

    " + }, + "Description": { + "shape": "Description", + "documentation": "

    The description for the data set.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set.

    " + }, + "Name": { + "shape": "Name", + "documentation": "

    The name of the data set.

    " + }, + "Origin": { + "shape": "Origin", + "documentation": "

    A property that defines the data set as OWNED by the account (for providers) or ENTITLED to the account (for subscribers).

    " + }, + "OriginDetails": { + "shape": "OriginDetails", + "documentation": "

    If the origin of this data set is ENTITLED, includes the details for the product on AWS Marketplace.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The data set ID of the owned data set corresponding to the entitled data set being viewed. This parameter is returned when a data set owner is viewing the entitled copy of its owned data set.

    " + }, + "Tags": { + "shape": "MapOf__string", + "documentation": "

    The tags for the data set.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was last updated, in ISO 8601 format.

    " + } + } + }, + "GetJobRequest": { + "type": "structure", + "members": { + "JobId": { + "shape": "__string", + "location": "uri", + "locationName": "JobId", + "documentation": "

    The unique identifier for a job.

    " + } + }, + "required": [ + "JobId" + ] + }, + "GetJobResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the job.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the job was created, in ISO 8601 format.

    " + }, + "Details": { + "shape": "ResponseDetails", + "documentation": "

    Details about the job.

    " + }, + "Errors": { + "shape": "ListOfJobError", + "documentation": "

    The errors associated with jobs.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the job.

    " + }, + "State": { + "shape": "State", + "documentation": "

    The state of the job.

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The job type.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the job was last updated, in ISO 8601 format.

    " + } + } + }, + "GetRevisionRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "required": [ + "RevisionId", + "DataSetId" + ] + }, + "GetRevisionResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the revision

    " + }, + "Comment": { + "shape": "__stringMin0Max16384", + "documentation": "

    An optional comment about the revision.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this revision.

    " + }, + "Finalized": { + "shape": "__boolean", + "documentation": "

    To publish a revision to a data set in a product, the revision must first be finalized. Finalizing a revision tells AWS Data Exchange that your changes to the assets in the revision are complete. After it's in this read-only state, you can publish the revision to your products.

    Finalized revisions can be published through the AWS Data Exchange console or the AWS Marketplace Catalog API, using the StartChangeSet AWS Marketplace Catalog API action. When using the API, revisions are uniquely identified by their ARN.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The revision ID of the owned revision corresponding to the entitled revision being viewed. This parameter is returned when a revision owner is viewing the entitled copy of its owned revision.

    " + }, + "Tags": { + "shape": "MapOf__string", + "documentation": "

    The tags for the revision.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was last updated, in ISO 8601 format.

    " + } + } + }, + "Id": { + "type": "string", + "documentation": "

    A unique identifier.

    " + }, + "ImportAssetFromSignedUrlJobErrorDetails": { + "type": "structure", + "members": { + "AssetName": { + "shape": "AssetName" + } + }, + "required": [ + "AssetName" + ] + }, + "ImportAssetFromSignedUrlRequestDetails": { + "type": "structure", + "members": { + "AssetName": { + "shape": "AssetName", + "documentation": "

    The name of the asset. When importing from Amazon S3, the S3 object key is used as the asset name.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this import job.

    " + }, + "Md5Hash": { + "shape": "__stringMin24Max24PatternAZaZ094AZaZ092AZaZ093", + "documentation": "

    The Base64-encoded Md5 hash for the asset, used to ensure the integrity of the file at that location.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this import request.

    " + } + }, + "documentation": "

    Details of the operation to be performed by the job.

    ", + "required": [ + "DataSetId", + "Md5Hash", + "RevisionId", + "AssetName" + ] + }, + "ImportAssetFromSignedUrlResponseDetails": { + "type": "structure", + "members": { + "AssetName": { + "shape": "AssetName", + "documentation": "

    The name for the asset associated with this import response.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this import job.

    " + }, + "Md5Hash": { + "shape": "__stringMin24Max24PatternAZaZ094AZaZ092AZaZ093", + "documentation": "

    The Base64-encoded Md5 hash for the asset, used to ensure the integrity of the file at that location.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this import response.

    " + }, + "SignedUrl": { + "shape": "__string", + "documentation": "

    The signed URL.

    " + }, + "SignedUrlExpiresAt": { + "shape": "Timestamp", + "documentation": "

    The time and date at which the signed URL expires, in ISO 8601 format.

    " + } + }, + "documentation": "

    The details in the response for an import request, including the signed URL and other information.

    ", + "required": [ + "DataSetId", + "AssetName", + "RevisionId" + ] + }, + "ImportAssetsFromS3RequestDetails": { + "type": "structure", + "members": { + "AssetSources": { + "shape": "ListOfAssetSourceEntry", + "documentation": "

    Is a list of S3 bucket and object key pairs.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this import job.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this import request.

    " + } + }, + "documentation": "

    Details of the operation to be performed by the job.

    ", + "required": [ + "DataSetId", + "AssetSources", + "RevisionId" + ] + }, + "ImportAssetsFromS3ResponseDetails": { + "type": "structure", + "members": { + "AssetSources": { + "shape": "ListOfAssetSourceEntry", + "documentation": "

    Is a list of Amazon S3 bucket and object key pairs.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this import job.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this import response.

    " + } + }, + "documentation": "

    Details from an import from Amazon S3 response.

    ", + "required": [ + "DataSetId", + "AssetSources", + "RevisionId" + ] + }, + "InternalServerException": { + "type": "structure", + "members": { + "Message": { + "shape": "__string", + "documentation": "The message identifying the service exception that occurred." + } + }, + "documentation": "An exception occurred with the service.", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 500 + } + }, + "JobEntry": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the job.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the job was created, in ISO 8601 format.

    " + }, + "Details": { + "shape": "ResponseDetails", + "documentation": "

    Details of the operation to be performed by the job, such as export destination details or import source details.

    " + }, + "Errors": { + "shape": "ListOfJobError", + "documentation": "

    Errors for jobs.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the job.

    " + }, + "State": { + "shape": "State", + "documentation": "

    The state of the job.

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The job type.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the job was last updated, in ISO 8601 format.

    " + } + }, + "documentation": "AWS Data Exchange Jobs are asynchronous import or export operations used to create or copy assets. A data set owner can both import and export as they see fit. Someone with an entitlement to a data set can only export. Jobs are deleted 90 days after they are created.", + "required": [ + "Type", + "Details", + "State", + "CreatedAt", + "Id", + "Arn", + "UpdatedAt" + ] + }, + "JobError": { + "type": "structure", + "members": { + "Code": { + "shape": "Code", + "documentation": "The code for the job error." + }, + "Details": { + "shape": "Details" + }, + "LimitName": { + "shape": "JobErrorLimitName", + "documentation": "

    The name of the limit that was reached.

    " + }, + "LimitValue": { + "shape": "__double", + "documentation": "The value of the exceeded limit." + }, + "Message": { + "shape": "__string", + "documentation": "The message related to the job error." + }, + "ResourceId": { + "shape": "__string", + "documentation": "The unqiue identifier for the resource related to the error." + }, + "ResourceType": { + "shape": "JobErrorResourceTypes", + "documentation": "The type of resource related to the error." + } + }, + "documentation": "An error that occurred with the job request.", + "required": [ + "Message", + "Code" + ] + }, + "JobErrorLimitName": { + "type": "string", + "documentation": "The name of the limit that was reached.", + "enum": [ + "Assets per revision", + "Asset size in GB" + ] + }, + "JobErrorResourceTypes": { + "type": "string", + "documentation": "The types of resource which the job error can apply to.", + "enum": [ + "REVISION", + "ASSET" + ] + }, + "LimitName": { + "type": "string", + "enum": [ + "Products per account", + "Data sets per account", + "Data sets per product", + "Revisions per data set", + "Assets per revision", + "Assets per import job from Amazon S3", + "Asset per export job from Amazon S3", + "Asset size in GB", + "Concurrent in progress jobs to import assets from Amazon S3", + "Concurrent in progress jobs to import assets from a signed URL", + "Concurrent in progress jobs to export assets to Amazon S3", + "Concurrent in progress jobs to export assets to a signed URL" + ] + }, + "ListDataSetRevisionsRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "MaxResults": { + "shape": "MaxResults", + "location": "querystring", + "locationName": "maxResults", + "documentation": "

    The maximum number of results returned by a single call.

    " + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + } + }, + "required": [ + "DataSetId" + ] + }, + "ListDataSetRevisionsResponse": { + "type": "structure", + "members": { + "NextToken": { + "shape": "NextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + }, + "Revisions": { + "shape": "ListOfRevisionEntry", + "documentation": "

    The asset objects listed by the request.

    " + } + } + }, + "ListDataSetsRequest": { + "type": "structure", + "members": { + "MaxResults": { + "shape": "MaxResults", + "location": "querystring", + "locationName": "maxResults", + "documentation": "

    The maximum number of results returned by a single call.

    " + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + }, + "Origin": { + "shape": "__string", + "location": "querystring", + "locationName": "origin", + "documentation": "

    A property that defines the data set as OWNED by the account (for providers) or ENTITLED to the account (for subscribers).

    " + } + } + }, + "ListDataSetsResponse": { + "type": "structure", + "members": { + "DataSets": { + "shape": "ListOfDataSetEntry", + "documentation": "

    The data set objects listed by the request.

    " + }, + "NextToken": { + "shape": "NextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + } + } + }, + "ListJobsRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "querystring", + "locationName": "dataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "MaxResults": { + "shape": "MaxResults", + "location": "querystring", + "locationName": "maxResults", + "documentation": "

    The maximum number of results returned by a single call.

    " + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "querystring", + "locationName": "revisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + } + }, + "ListJobsResponse": { + "type": "structure", + "members": { + "Jobs": { + "shape": "ListOfJobEntry", + "documentation": "

    The jobs listed by the request.

    " + }, + "NextToken": { + "shape": "NextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + } + } + }, + "ListOfAssetDestinationEntry": { + "type": "list", + "documentation": "

    The destination where the assets will be exported.

    ", + "member": { + "shape": "AssetDestinationEntry" + } + }, + "ListOfAssetSourceEntry": { + "type": "list", + "documentation": "

    The list of sources for the assets.

    ", + "member": { + "shape": "AssetSourceEntry" + } + }, + "ListRevisionAssetsRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "MaxResults": { + "shape": "MaxResults", + "location": "querystring", + "locationName": "maxResults", + "documentation": "

    The maximum number of results returned by a single call.

    " + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "required": [ + "RevisionId", + "DataSetId" + ] + }, + "ListRevisionAssetsResponse": { + "type": "structure", + "members": { + "Assets": { + "shape": "ListOfAssetEntry", + "documentation": "

    The asset objects listed by the request.

    " + }, + "NextToken": { + "shape": "NextToken", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + } + } + }, + "ListTagsForResourceRequest": { + "type": "structure", + "members": { + "ResourceArn": { + "shape": "__string", + "location": "uri", + "locationName": "resource-arn", + "documentation": "

    An Amazon Resource Name (ARN) that uniquely identifies an AWS resource.

    " + } + }, + "required": [ + "ResourceArn" + ] + }, + "ListTagsForResourceResponse": { + "type": "structure", + "members": { + "Tags": { + "shape": "MapOf__string", + "locationName": "tags", + "documentation": "A label that consists of a customer-defined key and an optional value." + } + } + }, + "MaxResults": { + "type": "integer", + "min": 1, + "max": 25 + }, + "Name": { + "type": "string", + "documentation": "The name of the model." + }, + "NextToken": { + "type": "string", + "documentation": "

    The token value retrieved from a previous call to access the next page of results.

    " + }, + "Origin": { + "type": "string", + "documentation": "

    A property that defines the data set as OWNED by the account (for providers) or ENTITLED to the account (for subscribers). When an owned data set is published in a product, AWS Data Exchange creates a copy of the data set. Subscribers can access that copy of the data set as an entitled data set.

    ", + "enum": [ + "OWNED", + "ENTITLED" + ] + }, + "OriginDetails": { + "type": "structure", + "members": { + "ProductId": { + "shape": "__string" + } + }, + "required": [ + "ProductId" + ] + }, + "RequestDetails": { + "type": "structure", + "members": { + "ExportAssetToSignedUrl": { + "shape": "ExportAssetToSignedUrlRequestDetails", + "documentation": "

    Details about the export to signed URL request.

    " + }, + "ExportAssetsToS3": { + "shape": "ExportAssetsToS3RequestDetails", + "documentation": "

    Details about the export to Amazon S3 request.

    " + }, + "ImportAssetFromSignedUrl": { + "shape": "ImportAssetFromSignedUrlRequestDetails", + "documentation": "

    Details about the import from signed URL request.

    " + }, + "ImportAssetsFromS3": { + "shape": "ImportAssetsFromS3RequestDetails", + "documentation": "

    Details about the import from Amazon S3 request.

    " + } + }, + "documentation": "

    The details for the request.

    " + }, + "ResourceNotFoundException": { + "type": "structure", + "members": { + "Message": { + "shape": "__string", + "documentation": "

    The resource couldn't be found.

    " + }, + "ResourceId": { + "shape": "__string", + "documentation": "

    The unique identifier for the resource that couldn't be found.

    " + }, + "ResourceType": { + "shape": "ResourceType", + "documentation": "

    The type of resource that couldn't be found.

    " + } + }, + "documentation": "

    The resource couldn't be found.

    ", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 404 + } + }, + "ResourceType": { + "type": "string", + "enum": [ + "DATA_SET", + "REVISION", + "ASSET", + "JOB" + ] + }, + "ResponseDetails": { + "type": "structure", + "members": { + "ExportAssetToSignedUrl": { + "shape": "ExportAssetToSignedUrlResponseDetails", + "documentation": "

    Details for the export to signed URL response.

    " + }, + "ExportAssetsToS3": { + "shape": "ExportAssetsToS3ResponseDetails", + "documentation": "

    Details for the export to Amazon S3 response.

    " + }, + "ImportAssetFromSignedUrl": { + "shape": "ImportAssetFromSignedUrlResponseDetails", + "documentation": "

    Details for the import from signed URL response.

    " + }, + "ImportAssetsFromS3": { + "shape": "ImportAssetsFromS3ResponseDetails", + "documentation": "

    Details for the import from Amazon S3 response.

    " + } + }, + "documentation": "

    Details for the response.

    " + }, + "RevisionEntry": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the revision.

    " + }, + "Comment": { + "shape": "__stringMin0Max16384", + "documentation": "

    An optional comment about the revision.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this revision.

    " + }, + "Finalized": { + "shape": "__boolean", + "documentation": "

    To publish a revision to a data set in a product, the revision must first be finalized. Finalizing a revision tells AWS Data Exchange that your changes to the assets in the revision are complete. After it's in this read-only state, you can publish the revision to your products.

    Finalized revisions can be published through the AWS Data Exchange console or the AWS Marketplace Catalog API, using the StartChangeSet AWS Marketplace Catalog API action. When using the API, revisions are uniquely identified by their ARN.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The revision ID of the owned revision corresponding to the entitled revision being viewed. This parameter is returned when a revision owner is viewing the entitled copy of its owned revision.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was last updated, in ISO 8601 format.

    " + } + }, + "documentation": "

    A revision is a container for one or more assets.

    ", + "required": [ + "CreatedAt", + "DataSetId", + "Id", + "Arn", + "UpdatedAt" + ] + }, + "S3SnapshotAsset": { + "type": "structure", + "members": { + "Size": { + "shape": "__doubleMin0", + "documentation": "

    The size of the S3 object that is the object.

    " + } + }, + "documentation": "

    The S3 object that is the asset.

    ", + "required": [ + "Size" + ] + }, + "ServiceLimitExceededException": { + "type": "structure", + "members": { + "LimitName": { + "shape": "LimitName", + "documentation": "

    The name of the quota that was exceeded.

    " + }, + "LimitValue": { + "shape": "__double", + "documentation": "

    The maximum value for the service-specific limit.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The request has exceeded the quotas imposed by the service.

    " + } + }, + "documentation": "

    The request has exceeded the quotas imposed by the service.

    ", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 402 + } + }, + "StartJobRequest": { + "type": "structure", + "members": { + "JobId": { + "shape": "__string", + "location": "uri", + "locationName": "JobId", + "documentation": "

    The unique identifier for a job.

    " + } + }, + "required": [ + "JobId" + ] + }, + "StartJobResponse": { + "type": "structure", + "members": {} + }, + "State": { + "type": "string", + "enum": [ + "WAITING", + "IN_PROGRESS", + "ERROR", + "COMPLETED", + "CANCELLED", + "TIMED_OUT" + ] + }, + "TagResourceRequest": { + "type": "structure", + "members": { + "ResourceArn": { + "shape": "__string", + "location": "uri", + "locationName": "resource-arn", + "documentation": "

    An Amazon Resource Name (ARN) that uniquely identifies an AWS resource.

    " + }, + "Tags": { + "shape": "MapOf__string", + "locationName": "tags", + "documentation": "A label that consists of a customer-defined key and an optional value." + } + }, + "documentation": "

    The request body for TagResource.

    ", + "required": [ + "ResourceArn", + "Tags" + ] + }, + "ThrottlingException": { + "type": "structure", + "members": { + "Message": { + "shape": "__string", + "documentation": "

    The limit on the number of requests per second was exceeded.

    " + } + }, + "documentation": "

    The limit on the number of requests per second was exceeded.

    ", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 429 + } + }, + "Timestamp": { + "type": "timestamp", + "documentation": "

    Dates and times in AWS Data Exchange are recorded in ISO 8601 format.

    ", + "timestampFormat": "iso8601" + }, + "Type": { + "type": "string", + "enum": [ + "IMPORT_ASSETS_FROM_S3", + "IMPORT_ASSET_FROM_SIGNED_URL", + "EXPORT_ASSETS_TO_S3", + "EXPORT_ASSET_TO_SIGNED_URL" + ] + }, + "UntagResourceRequest": { + "type": "structure", + "members": { + "ResourceArn": { + "shape": "__string", + "location": "uri", + "locationName": "resource-arn", + "documentation": "

    An Amazon Resource Name (ARN) that uniquely identifies an AWS resource.

    " + }, + "TagKeys": { + "shape": "ListOf__string", + "location": "querystring", + "locationName": "tagKeys", + "documentation": "The key tags." + } + }, + "required": [ + "TagKeys", + "ResourceArn" + ] + }, + "UpdateAssetRequest": { + "type": "structure", + "members": { + "AssetId": { + "shape": "__string", + "location": "uri", + "locationName": "AssetId", + "documentation": "

    The unique identifier for an asset.

    " + }, + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "Name": { + "shape": "AssetName", + "documentation": "

    The name of the asset. When importing from Amazon S3, the S3 object key is used as the asset name. When exporting to Amazon S3, the asset name is used as default target S3 object key.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "documentation": "

    The request body for UpdateAsset.

    ", + "required": [ + "RevisionId", + "AssetId", + "DataSetId", + "Name" + ] + }, + "UpdateAssetResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the asset.

    " + }, + "AssetDetails": { + "shape": "AssetDetails", + "documentation": "

    Information about the asset, including its size.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the asset was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this asset.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the asset.

    " + }, + "Name": { + "shape": "AssetName", + "documentation": "

    The name of the asset When importing from Amazon S3, the S3 object key is used as the asset name. When exporting to Amazon S3, the asset name is used as default target S3 object key.

    " + }, + "RevisionId": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision associated with this asset.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The asset ID of the owned asset corresponding to the entitled asset being viewed. This parameter is returned when an asset owner is viewing the entitled copy of its owned asset.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the asset was last updated, in ISO 8601 format.

    " + } + } + }, + "UpdateDataSetRequest": { + "type": "structure", + "members": { + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "Description": { + "shape": "Description", + "documentation": "

    The description for the data set.

    " + }, + "Name": { + "shape": "Name", + "documentation": "

    The name of the data set.

    " + } + }, + "documentation": "

    The request body for UpdateDataSet.

    ", + "required": [ + "DataSetId" + ] + }, + "UpdateDataSetResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the data set.

    " + }, + "AssetType": { + "shape": "AssetType", + "documentation": "

    The type of file your data is stored in. Currently, the supported asset type is S3_SNAPSHOT.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was created, in ISO 8601 format.

    " + }, + "Description": { + "shape": "Description", + "documentation": "

    The description for the data set.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set.

    " + }, + "Name": { + "shape": "Name", + "documentation": "

    The name of the data set.

    " + }, + "Origin": { + "shape": "Origin", + "documentation": "

    A property that defines the data set as OWNED by the account (for providers) or ENTITLED to the account (for subscribers).

    " + }, + "OriginDetails": { + "shape": "OriginDetails", + "documentation": "

    If the origin of this data set is ENTITLED, includes the details for the product on AWS Marketplace.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The data set ID of the owned data set corresponding to the entitled data set being viewed. This parameter is returned when a data set owner is viewing the entitled copy of its owned data set.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the data set was last updated, in ISO 8601 format.

    " + } + } + }, + "UpdateRevisionRequest": { + "type": "structure", + "members": { + "Comment": { + "shape": "__stringMin0Max16384", + "documentation": "

    An optional comment about the revision.

    " + }, + "DataSetId": { + "shape": "__string", + "location": "uri", + "locationName": "DataSetId", + "documentation": "

    The unique identifier for a data set.

    " + }, + "Finalized": { + "shape": "__boolean", + "documentation": "

    Finalizing a revision tells AWS Data Exchange that your changes to the assets in the revision are complete. After it's in this read-only state, you can publish the revision to your products.

    " + }, + "RevisionId": { + "shape": "__string", + "location": "uri", + "locationName": "RevisionId", + "documentation": "

    The unique identifier for a revision.

    " + } + }, + "documentation": "

    The request body for UpdateRevision.

    ", + "required": [ + "RevisionId", + "DataSetId" + ] + }, + "UpdateRevisionResponse": { + "type": "structure", + "members": { + "Arn": { + "shape": "Arn", + "documentation": "

    The ARN for the revision.

    " + }, + "Comment": { + "shape": "__stringMin0Max16384", + "documentation": "

    An optional comment about the revision.

    " + }, + "CreatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was created, in ISO 8601 format.

    " + }, + "DataSetId": { + "shape": "Id", + "documentation": "

    The unique identifier for the data set associated with this revision.

    " + }, + "Finalized": { + "shape": "__boolean", + "documentation": "

    To publish a revision to a data set in a product, the revision must first be finalized. Finalizing a revision tells AWS Data Exchange that changes to the assets in the revision are complete. After it's in this read-only state, you can publish the revision to your products.

    Finalized revisions can be published through the AWS Data Exchange console or the AWS Marketplace Catalog API, using the StartChangeSet AWS Marketplace Catalog API action. When using the API, revisions are uniquely identified by their ARN.

    " + }, + "Id": { + "shape": "Id", + "documentation": "

    The unique identifier for the revision.

    " + }, + "SourceId": { + "shape": "Id", + "documentation": "

    The revision ID of the owned revision corresponding to the entitled revision being viewed. This parameter is returned when a revision owner is viewing the entitled copy of its owned revision.

    " + }, + "UpdatedAt": { + "shape": "Timestamp", + "documentation": "

    The date and time that the revision was last updated, in ISO 8601 format.

    " + } + } + }, + "ValidationException": { + "type": "structure", + "members": { + "Message": { + "shape": "__string", + "documentation": "

    The message that informs you about what was invalid about the request.

    " + } + }, + "documentation": "

    The request was invalid.

    ", + "required": [ + "Message" + ], + "exception": true, + "error": { + "httpStatusCode": 400 + } + }, + "__boolean": { + "type": "boolean" + }, + "__double": { + "type": "double" + }, + "__doubleMin0": { + "type": "double" + }, + "ListOfAssetEntry": { + "type": "list", + "member": { + "shape": "AssetEntry" + } + }, + "ListOfDataSetEntry": { + "type": "list", + "member": { + "shape": "DataSetEntry" + } + }, + "ListOfJobEntry": { + "type": "list", + "member": { + "shape": "JobEntry" + } + }, + "ListOfJobError": { + "type": "list", + "member": { + "shape": "JobError" + } + }, + "ListOfRevisionEntry": { + "type": "list", + "member": { + "shape": "RevisionEntry" + } + }, + "ListOf__string": { + "type": "list", + "member": { + "shape": "__string" + } + }, + "MapOf__string": { + "type": "map", + "key": { + "shape": "__string" + }, + "value": { + "shape": "__string" + } + }, + "__string": { + "type": "string" + }, + "__stringMin0Max16384": { + "type": "string", + "min": 0, + "max": 16384 + }, + "__stringMin24Max24PatternAZaZ094AZaZ092AZaZ093": { + "type": "string", + "min": 24, + "max": 24, + "pattern": "/^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)?$/" + } + }, + "authorizers": { + "create_job_authorizer": { + "name": "create_job_authorizer", + "type": "provided", + "placement": { + "location": "header", + "name": "Authorization" + } + }, + "start_cancel_get_job_authorizer": { + "name": "start_cancel_get_job_authorizer", + "type": "provided", + "placement": { + "location": "header", + "name": "Authorization" + } + } + }, + "documentation": "

    AWS Data Exchange is a service that makes it easy for AWS customers to exchange data in the cloud. You can use the AWS Data Exchange APIs to create, update, manage, and access file-based data set in the AWS Cloud.

    As a subscriber, you can view and access the data sets that you have an entitlement to through a subscription. You can use the APIS to download or copy your entitled data sets to Amazon S3 for use across a variety of AWS analytics and machine learning services.

    As a provider, you can create and manage your data sets that you would like to publish to a product. Being able to package and provide your data sets into products requires a few steps to determine eligibility. For more information, visit the AWS Data Exchange User Guide.

    A data set is a collection of data that can be changed or updated over time. Data sets can be updated using revisions, which represent a new version or incremental change to a data set. A revision contains one or more assets. An asset in AWS Data Exchange is a piece of data that can be stored as an Amazon S3 object. The asset can be a structured data file, an image file, or some other data file. Jobs are asynchronous import or export operations used to create or copy assets.

    " +} \ No newline at end of file diff --git a/services/datapipeline/build.properties b/services/datapipeline/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/datapipeline/build.properties +++ b/services/datapipeline/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/datapipeline/pom.xml b/services/datapipeline/pom.xml index 42668f4871e7..6506b32038a1 100644 --- a/services/datapipeline/pom.xml +++ b/services/datapipeline/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + detective + AWS Java SDK :: Services :: Detective + The AWS Java SDK for Detective module holds the client classes that are used for + communicating with Detective. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.detective + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/detective/src/main/resources/codegen-resources/paginators-1.json b/services/detective/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..8ead136f5ee8 --- /dev/null +++ b/services/detective/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,19 @@ +{ + "pagination": { + "ListGraphs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListInvitations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListMembers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/detective/src/main/resources/codegen-resources/service-2.json b/services/detective/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..67d527b795c3 --- /dev/null +++ b/services/detective/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,655 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2018-10-26", + "endpointPrefix":"api.detective", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceFullName":"Amazon Detective", + "serviceId":"Detective", + "signatureVersion":"v4", + "signingName":"detective", + "uid":"detective-2018-10-26" + }, + "operations":{ + "AcceptInvitation":{ + "name":"AcceptInvitation", + "http":{ + "method":"PUT", + "requestUri":"/invitation" + }, + "input":{"shape":"AcceptInvitationRequest"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Accepts an invitation for the member account to contribute data to a behavior graph. This operation can only be called by an invited member account.

    The request provides the ARN of behavior graph.

    The member account status in the graph must be INVITED.

    " + }, + "CreateGraph":{ + "name":"CreateGraph", + "http":{ + "method":"POST", + "requestUri":"/graph" + }, + "output":{"shape":"CreateGraphResponse"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceQuotaExceededException"} + ], + "documentation":"

    Creates a new behavior graph for the calling account, and sets that account as the master account. This operation is called by the account that is enabling Detective.

    Before you try to enable Detective, make sure that your account has been enrolled in Amazon GuardDuty for at least 48 hours. If you do not meet this requirement, you cannot enable Detective. If you do meet the GuardDuty prerequisite, then when you make the request to enable Detective, it checks whether your data volume is within the Detective quota. If it exceeds the quota, then you cannot enable Detective.

    The operation also enables Detective for the calling account in the currently selected Region. It returns the ARN of the new behavior graph.

    CreateGraph triggers a process to create the corresponding data tables for the new behavior graph.

    An account can only be the master account for one behavior graph within a Region. If the same account calls CreateGraph with the same master account, it always returns the same behavior graph ARN. It does not create a new behavior graph.

    " + }, + "CreateMembers":{ + "name":"CreateMembers", + "http":{ + "method":"POST", + "requestUri":"/graph/members" + }, + "input":{"shape":"CreateMembersRequest"}, + "output":{"shape":"CreateMembersResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"} + ], + "documentation":"

    Sends a request to invite the specified AWS accounts to be member accounts in the behavior graph. This operation can only be called by the master account for a behavior graph.

    CreateMembers verifies the accounts and then sends invitations to the verified accounts.

    The request provides the behavior graph ARN and the list of accounts to invite.

    The response separates the requested accounts into two lists:

    • The accounts that CreateMembers was able to start the verification for. This list includes member accounts that are being verified, that have passed verification and are being sent an invitation, and that have failed verification.

    • The accounts that CreateMembers was unable to process. This list includes accounts that were already invited to be member accounts in the behavior graph.

    " + }, + "DeleteGraph":{ + "name":"DeleteGraph", + "http":{ + "method":"POST", + "requestUri":"/graph/removal" + }, + "input":{"shape":"DeleteGraphRequest"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Disables the specified behavior graph and queues it to be deleted. This operation removes the graph from each member account's list of behavior graphs.

    DeleteGraph can only be called by the master account for a behavior graph.

    " + }, + "DeleteMembers":{ + "name":"DeleteMembers", + "http":{ + "method":"POST", + "requestUri":"/graph/members/removal" + }, + "input":{"shape":"DeleteMembersRequest"}, + "output":{"shape":"DeleteMembersResponse"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Deletes one or more member accounts from the master account behavior graph. This operation can only be called by a Detective master account. That account cannot use DeleteMembers to delete their own account from the behavior graph. To disable a behavior graph, the master account uses the DeleteGraph API method.

    " + }, + "DisassociateMembership":{ + "name":"DisassociateMembership", + "http":{ + "method":"POST", + "requestUri":"/membership/removal" + }, + "input":{"shape":"DisassociateMembershipRequest"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Removes the member account from the specified behavior graph. This operation can only be called by a member account that has the ENABLED status.

    " + }, + "GetMembers":{ + "name":"GetMembers", + "http":{ + "method":"POST", + "requestUri":"/graph/members/get" + }, + "input":{"shape":"GetMembersRequest"}, + "output":{"shape":"GetMembersResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns the membership details for specified member accounts for a behavior graph.

    " + }, + "ListGraphs":{ + "name":"ListGraphs", + "http":{ + "method":"POST", + "requestUri":"/graphs/list" + }, + "input":{"shape":"ListGraphsRequest"}, + "output":{"shape":"ListGraphsResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Returns the list of behavior graphs that the calling account is a master of. This operation can only be called by a master account.

    Because an account can currently only be the master of one behavior graph within a Region, the results always contain a single graph.

    " + }, + "ListInvitations":{ + "name":"ListInvitations", + "http":{ + "method":"POST", + "requestUri":"/invitations/list" + }, + "input":{"shape":"ListInvitationsRequest"}, + "output":{"shape":"ListInvitationsResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Retrieves the list of open and accepted behavior graph invitations for the member account. This operation can only be called by a member account.

    Open invitations are invitations that the member account has not responded to.

    The results do not include behavior graphs for which the member account declined the invitation. The results also do not include behavior graphs that the member account resigned from or was removed from.

    " + }, + "ListMembers":{ + "name":"ListMembers", + "http":{ + "method":"POST", + "requestUri":"/graph/members/list" + }, + "input":{"shape":"ListMembersRequest"}, + "output":{"shape":"ListMembersResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Retrieves the list of member accounts for a behavior graph. Does not return member accounts that were removed from the behavior graph.

    " + }, + "RejectInvitation":{ + "name":"RejectInvitation", + "http":{ + "method":"POST", + "requestUri":"/invitation/removal" + }, + "input":{"shape":"RejectInvitationRequest"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Rejects an invitation to contribute the account data to a behavior graph. This operation must be called by a member account that has the INVITED status.

    " + }, + "StartMonitoringMember":{ + "name":"StartMonitoringMember", + "http":{ + "method":"POST", + "requestUri":"/graph/member/monitoringstate" + }, + "input":{"shape":"StartMonitoringMemberRequest"}, + "errors":[ + {"shape":"ConflictException"}, + {"shape":"InternalServerException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Sends a request to enable data ingest for a member account that has a status of ACCEPTED_BUT_DISABLED.

    For valid member accounts, the status is updated as follows.

    • If Detective enabled the member account, then the new status is ENABLED.

    • If Detective cannot enable the member account, the status remains ACCEPTED_BUT_DISABLED.

    " + } + }, + "shapes":{ + "AcceptInvitationRequest":{ + "type":"structure", + "required":["GraphArn"], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph that the member account is accepting the invitation for.

    The member account status in the behavior graph must be INVITED.

    " + } + } + }, + "Account":{ + "type":"structure", + "required":[ + "AccountId", + "EmailAddress" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account identifier of the AWS account.

    " + }, + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The AWS account root user email address for the AWS account.

    " + } + }, + "documentation":"

    An AWS account that is the master of or a member of a behavior graph.

    " + }, + "AccountId":{ + "type":"string", + "max":12, + "min":12, + "pattern":"^[0-9]+$" + }, + "AccountIdList":{ + "type":"list", + "member":{"shape":"AccountId"}, + "max":50, + "min":1 + }, + "AccountList":{ + "type":"list", + "member":{"shape":"Account"}, + "max":50, + "min":1 + }, + "ConflictException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request attempted an invalid action.

    ", + "error":{"httpStatusCode":409}, + "exception":true + }, + "CreateGraphResponse":{ + "type":"structure", + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the new behavior graph.

    " + } + } + }, + "CreateMembersRequest":{ + "type":"structure", + "required":[ + "GraphArn", + "Accounts" + ], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph to invite the member accounts to contribute their data to.

    " + }, + "Message":{ + "shape":"EmailMessage", + "documentation":"

    Customized message text to include in the invitation email message to the invited member accounts.

    " + }, + "Accounts":{ + "shape":"AccountList", + "documentation":"

    The list of AWS accounts to invite to become member accounts in the behavior graph. For each invited account, the account list contains the account identifier and the AWS account root user email address.

    " + } + } + }, + "CreateMembersResponse":{ + "type":"structure", + "members":{ + "Members":{ + "shape":"MemberDetailList", + "documentation":"

    The set of member account invitation requests that Detective was able to process. This includes accounts that are being verified, that failed verification, and that passed verification and are being sent an invitation.

    " + }, + "UnprocessedAccounts":{ + "shape":"UnprocessedAccountList", + "documentation":"

    The list of accounts for which Detective was unable to process the invitation request. For each account, the list provides the reason why the request could not be processed. The list includes accounts that are already member accounts in the behavior graph.

    " + } + } + }, + "DeleteGraphRequest":{ + "type":"structure", + "required":["GraphArn"], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph to disable.

    " + } + } + }, + "DeleteMembersRequest":{ + "type":"structure", + "required":[ + "GraphArn", + "AccountIds" + ], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph to delete members from.

    " + }, + "AccountIds":{ + "shape":"AccountIdList", + "documentation":"

    The list of AWS account identifiers for the member accounts to delete from the behavior graph.

    " + } + } + }, + "DeleteMembersResponse":{ + "type":"structure", + "members":{ + "AccountIds":{ + "shape":"AccountIdList", + "documentation":"

    The list of AWS account identifiers for the member accounts that Detective successfully deleted from the behavior graph.

    " + }, + "UnprocessedAccounts":{ + "shape":"UnprocessedAccountList", + "documentation":"

    The list of member accounts that Detective was not able to delete from the behavior graph. For each member account, provides the reason that the deletion could not be processed.

    " + } + } + }, + "DisassociateMembershipRequest":{ + "type":"structure", + "required":["GraphArn"], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph to remove the member account from.

    The member account's member status in the behavior graph must be ENABLED.

    " + } + } + }, + "EmailAddress":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^.+@.+$" + }, + "EmailMessage":{ + "type":"string", + "max":1000, + "min":1 + }, + "ErrorMessage":{"type":"string"}, + "GetMembersRequest":{ + "type":"structure", + "required":[ + "GraphArn", + "AccountIds" + ], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph for which to request the member details.

    " + }, + "AccountIds":{ + "shape":"AccountIdList", + "documentation":"

    The list of AWS account identifiers for the member account for which to return member details.

    You cannot use GetMembers to retrieve information about member accounts that were removed from the behavior graph.

    " + } + } + }, + "GetMembersResponse":{ + "type":"structure", + "members":{ + "MemberDetails":{ + "shape":"MemberDetailList", + "documentation":"

    The member account details that Detective is returning in response to the request.

    " + }, + "UnprocessedAccounts":{ + "shape":"UnprocessedAccountList", + "documentation":"

    The requested member accounts for which Detective was unable to return member details.

    For each account, provides the reason why the request could not be processed.

    " + } + } + }, + "Graph":{ + "type":"structure", + "members":{ + "Arn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph.

    " + }, + "CreatedTime":{ + "shape":"Timestamp", + "documentation":"

    The date and time that the behavior graph was created. The value is in milliseconds since the epoch.

    " + } + }, + "documentation":"

    A behavior graph in Detective.

    " + }, + "GraphArn":{ + "type":"string", + "pattern":"^arn:aws[-\\w]{0,10}?:detective:[-\\w]{2,20}?:\\d{12}?:graph:[abcdef\\d]{32}?$" + }, + "GraphList":{ + "type":"list", + "member":{"shape":"Graph"} + }, + "InternalServerException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request was valid but failed because of a problem with the service.

    ", + "error":{"httpStatusCode":500}, + "exception":true + }, + "ListGraphsRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"PaginationToken", + "documentation":"

    For requests to get the next page of results, the pagination token that was returned with the previous set of results. The initial request does not include a pagination token.

    " + }, + "MaxResults":{ + "shape":"MemberResultsLimit", + "documentation":"

    The maximum number of graphs to return at a time. The total must be less than the overall limit on the number of results to return, which is currently 200.

    " + } + } + }, + "ListGraphsResponse":{ + "type":"structure", + "members":{ + "GraphList":{ + "shape":"GraphList", + "documentation":"

    A list of behavior graphs that the account is a master for.

    " + }, + "NextToken":{ + "shape":"PaginationToken", + "documentation":"

    If there are more behavior graphs remaining in the results, then this is the pagination token to use to request the next page of behavior graphs.

    " + } + } + }, + "ListInvitationsRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"PaginationToken", + "documentation":"

    For requests to retrieve the next page of results, the pagination token that was returned with the previous page of results. The initial request does not include a pagination token.

    " + }, + "MaxResults":{ + "shape":"MemberResultsLimit", + "documentation":"

    The maximum number of behavior graph invitations to return in the response. The total must be less than the overall limit on the number of results to return, which is currently 200.

    " + } + } + }, + "ListInvitationsResponse":{ + "type":"structure", + "members":{ + "Invitations":{ + "shape":"MemberDetailList", + "documentation":"

    The list of behavior graphs for which the member account has open or accepted invitations.

    " + }, + "NextToken":{ + "shape":"PaginationToken", + "documentation":"

    If there are more behavior graphs remaining in the results, then this is the pagination token to use to request the next page of behavior graphs.

    " + } + } + }, + "ListMembersRequest":{ + "type":"structure", + "required":["GraphArn"], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph for which to retrieve the list of member accounts.

    " + }, + "NextToken":{ + "shape":"PaginationToken", + "documentation":"

    For requests to retrieve the next page of member account results, the pagination token that was returned with the previous page of results. The initial request does not include a pagination token.

    " + }, + "MaxResults":{ + "shape":"MemberResultsLimit", + "documentation":"

    The maximum number of member accounts to include in the response. The total must be less than the overall limit on the number of results to return, which is currently 200.

    " + } + } + }, + "ListMembersResponse":{ + "type":"structure", + "members":{ + "MemberDetails":{ + "shape":"MemberDetailList", + "documentation":"

    The list of member accounts in the behavior graph.

    The results include member accounts that did not pass verification and member accounts that have not yet accepted the invitation to the behavior graph. The results do not include member accounts that were removed from the behavior graph.

    " + }, + "NextToken":{ + "shape":"PaginationToken", + "documentation":"

    If there are more member accounts remaining in the results, then this is the pagination token to use to request the next page of member accounts.

    " + } + } + }, + "MemberDetail":{ + "type":"structure", + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account identifier for the member account.

    " + }, + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The AWS account root user email address for the member account.

    " + }, + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph that the member account was invited to.

    " + }, + "MasterId":{ + "shape":"AccountId", + "documentation":"

    The AWS account identifier of the master account for the behavior graph.

    " + }, + "Status":{ + "shape":"MemberStatus", + "documentation":"

    The current membership status of the member account. The status can have one of the following values:

    • INVITED - Indicates that the member was sent an invitation but has not yet responded.

    • VERIFICATION_IN_PROGRESS - Indicates that Detective is verifying that the account identifier and email address provided for the member account match. If they do match, then Detective sends the invitation. If the email address and account identifier don't match, then the member cannot be added to the behavior graph.

    • VERIFICATION_FAILED - Indicates that the account and email address provided for the member account do not match, and Detective did not send an invitation to the account.

    • ENABLED - Indicates that the member account accepted the invitation to contribute to the behavior graph.

    • ACCEPTED_BUT_DISABLED - Indicates that the member account accepted the invitation but is prevented from contributing data to the behavior graph. DisabledReason provides the reason why the member account is not enabled.

    Member accounts that declined an invitation or that were removed from the behavior graph are not included.

    " + }, + "DisabledReason":{ + "shape":"MemberDisabledReason", + "documentation":"

    For member accounts with a status of ACCEPTED_BUT_DISABLED, the reason that the member account is not enabled.

    The reason can have one of the following values:

    • VOLUME_TOO_HIGH - Indicates that adding the member account would cause the data volume for the behavior graph to be too high.

    • VOLUME_UNKNOWN - Indicates that Detective is unable to verify the data volume for the member account. This is usually because the member account is not enrolled in Amazon GuardDuty.

    " + }, + "InvitedTime":{ + "shape":"Timestamp", + "documentation":"

    The date and time that Detective sent the invitation to the member account. The value is in milliseconds since the epoch.

    " + }, + "UpdatedTime":{ + "shape":"Timestamp", + "documentation":"

    The date and time that the member account was last updated. The value is in milliseconds since the epoch.

    " + }, + "PercentOfGraphUtilization":{ + "shape":"Percentage", + "documentation":"

    The member account data volume as a percentage of the maximum allowed data volume. 0 indicates 0 percent, and 100 indicates 100 percent.

    Note that this is not the percentage of the behavior graph data volume.

    For example, the data volume for the behavior graph is 80 GB per day. The maximum data volume is 160 GB per day. If the data volume for the member account is 40 GB per day, then PercentOfGraphUtilization is 25. It represents 25% of the maximum allowed data volume.

    " + }, + "PercentOfGraphUtilizationUpdatedTime":{ + "shape":"Timestamp", + "documentation":"

    The date and time when the graph utilization percentage was last updated.

    " + } + }, + "documentation":"

    Details about a member account that was invited to contribute to a behavior graph.

    " + }, + "MemberDetailList":{ + "type":"list", + "member":{"shape":"MemberDetail"} + }, + "MemberDisabledReason":{ + "type":"string", + "enum":[ + "VOLUME_TOO_HIGH", + "VOLUME_UNKNOWN" + ] + }, + "MemberResultsLimit":{ + "type":"integer", + "box":true, + "max":200, + "min":1 + }, + "MemberStatus":{ + "type":"string", + "enum":[ + "INVITED", + "VERIFICATION_IN_PROGRESS", + "VERIFICATION_FAILED", + "ENABLED", + "ACCEPTED_BUT_DISABLED" + ] + }, + "PaginationToken":{ + "type":"string", + "max":1024, + "min":1 + }, + "Percentage":{"type":"double"}, + "RejectInvitationRequest":{ + "type":"structure", + "required":["GraphArn"], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph to reject the invitation to.

    The member account's current member status in the behavior graph must be INVITED.

    " + } + } + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request refers to a nonexistent resource.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    This request cannot be completed for one of the following reasons.

    • The request would cause the number of member accounts in the behavior graph to exceed the maximum allowed. A behavior graph cannot have more than 1000 member accounts.

    • The request would cause the data rate for the behavior graph to exceed the maximum allowed.

    • Detective is unable to verify the data rate for the member account. This is usually because the member account is not enrolled in Amazon GuardDuty.

    ", + "error":{"httpStatusCode":402}, + "exception":true + }, + "StartMonitoringMemberRequest":{ + "type":"structure", + "required":[ + "GraphArn", + "AccountId" + ], + "members":{ + "GraphArn":{ + "shape":"GraphArn", + "documentation":"

    The ARN of the behavior graph.

    " + }, + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID of the member account to try to enable.

    The account must be an invited member account with a status of ACCEPTED_BUT_DISABLED.

    " + } + } + }, + "Timestamp":{"type":"timestamp"}, + "UnprocessedAccount":{ + "type":"structure", + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account identifier of the member account that was not processed.

    " + }, + "Reason":{ + "shape":"UnprocessedReason", + "documentation":"

    The reason that the member account request could not be processed.

    " + } + }, + "documentation":"

    A member account that was included in a request but for which the request could not be processed.

    " + }, + "UnprocessedAccountList":{ + "type":"list", + "member":{"shape":"UnprocessedAccount"} + }, + "UnprocessedReason":{"type":"string"}, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The request parameters are invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + } + }, + "documentation":"

    Detective uses machine learning and purpose-built visualizations to help you analyze and investigate security issues across your Amazon Web Services (AWS) workloads. Detective automatically extracts time-based events such as login attempts, API calls, and network traffic from AWS CloudTrail and Amazon Virtual Private Cloud (Amazon VPC) flow logs. It also extracts findings detected by Amazon GuardDuty.

    The Detective API primarily supports the creation and management of behavior graphs. A behavior graph contains the extracted data from a set of member accounts, and is created and managed by a master account.

    Every behavior graph is specific to a Region. You can only use the API to manage graphs that belong to the Region that is associated with the currently selected endpoint.

    A Detective master account can use the Detective API to do the following:

    • Enable and disable Detective. Enabling Detective creates a new behavior graph.

    • View the list of member accounts in a behavior graph.

    • Add member accounts to a behavior graph.

    • Remove member accounts from a behavior graph.

    A member account can use the Detective API to do the following:

    • View the list of behavior graphs that they are invited to.

    • Accept an invitation to contribute to a behavior graph.

    • Decline an invitation to contribute to a behavior graph.

    • Remove their account from a behavior graph.

    All API actions are logged as CloudTrail events. See Logging Detective API Calls with CloudTrail.

    " +} diff --git a/services/devicefarm/build.properties b/services/devicefarm/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/devicefarm/build.properties +++ b/services/devicefarm/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/devicefarm/pom.xml b/services/devicefarm/pom.xml index 94fc97c76441..598d6a3864d6 100644 --- a/services/devicefarm/pom.xml +++ b/services/devicefarm/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + ebs + AWS Java SDK :: Services :: EBS + The AWS Java SDK for EBS module holds the client classes that are used for + communicating with EBS. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.ebs + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/ebs/src/main/resources/codegen-resources/paginators-1.json b/services/ebs/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..e8595e4f3c15 --- /dev/null +++ b/services/ebs/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,14 @@ +{ + "pagination": { + "ListChangedBlocks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListSnapshotBlocks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/ebs/src/main/resources/codegen-resources/service-2.json b/services/ebs/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..c562c9fbbe7a --- /dev/null +++ b/services/ebs/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,351 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-11-02", + "endpointPrefix":"ebs", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"Amazon EBS", + "serviceFullName":"Amazon Elastic Block Store", + "serviceId":"EBS", + "signatureVersion":"v4", + "uid":"ebs-2019-11-02" + }, + "operations":{ + "GetSnapshotBlock":{ + "name":"GetSnapshotBlock", + "http":{ + "method":"GET", + "requestUri":"/snapshots/{snapshotId}/blocks/{blockIndex}" + }, + "input":{"shape":"GetSnapshotBlockRequest"}, + "output":{"shape":"GetSnapshotBlockResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Returns the data in a block in an Amazon Elastic Block Store snapshot.

    " + }, + "ListChangedBlocks":{ + "name":"ListChangedBlocks", + "http":{ + "method":"GET", + "requestUri":"/snapshots/{secondSnapshotId}/changedblocks" + }, + "input":{"shape":"ListChangedBlocksRequest"}, + "output":{"shape":"ListChangedBlocksResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Returns the block indexes and block tokens for blocks that are different between two Amazon Elastic Block Store snapshots of the same volume/snapshot lineage.

    " + }, + "ListSnapshotBlocks":{ + "name":"ListSnapshotBlocks", + "http":{ + "method":"GET", + "requestUri":"/snapshots/{snapshotId}/blocks" + }, + "input":{"shape":"ListSnapshotBlocksRequest"}, + "output":{"shape":"ListSnapshotBlocksResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Returns the block indexes and block tokens for blocks in an Amazon Elastic Block Store snapshot.

    " + } + }, + "shapes":{ + "Block":{ + "type":"structure", + "members":{ + "BlockIndex":{ + "shape":"BlockIndex", + "documentation":"

    The block index.

    " + }, + "BlockToken":{ + "shape":"BlockToken", + "documentation":"

    The block token for the block index.

    " + } + }, + "documentation":"

    A block of data in an Amazon Elastic Block Store snapshot.

    " + }, + "BlockData":{ + "type":"blob", + "sensitive":true, + "streaming":true + }, + "BlockIndex":{"type":"integer"}, + "BlockSize":{"type":"integer"}, + "BlockToken":{ + "type":"string", + "max":256, + "pattern":"^[A-Za-z0-9+/=]+$" + }, + "Blocks":{ + "type":"list", + "member":{"shape":"Block"}, + "sensitive":true + }, + "ChangedBlock":{ + "type":"structure", + "members":{ + "BlockIndex":{ + "shape":"BlockIndex", + "documentation":"

    The block index.

    " + }, + "FirstBlockToken":{ + "shape":"BlockToken", + "documentation":"

    The block token for the block index of the FirstSnapshotId specified in the ListChangedBlocks operation. This value is absent if the first snapshot does not have the changed block that is on the second snapshot.

    " + }, + "SecondBlockToken":{ + "shape":"BlockToken", + "documentation":"

    The block token for the block index of the SecondSnapshotId specified in the ListChangedBlocks operation.

    " + } + }, + "documentation":"

    A block of data in an Amazon Elastic Block Store snapshot that is different from another snapshot of the same volume/snapshot lineage.

    ", + "sensitive":true + }, + "ChangedBlocks":{ + "type":"list", + "member":{"shape":"ChangedBlock"} + }, + "Checksum":{ + "type":"string", + "max":64 + }, + "ChecksumAlgorithm":{ + "type":"string", + "enum":["SHA256"], + "max":32 + }, + "DataLength":{"type":"integer"}, + "ErrorMessage":{ + "type":"string", + "max":256 + }, + "GetSnapshotBlockRequest":{ + "type":"structure", + "required":[ + "SnapshotId", + "BlockIndex", + "BlockToken" + ], + "members":{ + "SnapshotId":{ + "shape":"SnapshotId", + "documentation":"

    The ID of the snapshot containing the block from which to get data.

    ", + "location":"uri", + "locationName":"snapshotId" + }, + "BlockIndex":{ + "shape":"BlockIndex", + "documentation":"

    The block index of the block from which to get data.

    Obtain the BlockIndex by running the ListChangedBlocks or ListSnapshotBlocks operations.

    ", + "location":"uri", + "locationName":"blockIndex" + }, + "BlockToken":{ + "shape":"BlockToken", + "documentation":"

    The block token of the block from which to get data.

    Obtain the BlockToken by running the ListChangedBlocks or ListSnapshotBlocks operations.

    ", + "location":"querystring", + "locationName":"blockToken" + } + } + }, + "GetSnapshotBlockResponse":{ + "type":"structure", + "members":{ + "DataLength":{ + "shape":"DataLength", + "documentation":"

    The size of the data in the block.

    ", + "location":"header", + "locationName":"x-amz-Data-Length" + }, + "BlockData":{ + "shape":"BlockData", + "documentation":"

    The data content of the block.

    " + }, + "Checksum":{ + "shape":"Checksum", + "documentation":"

    The checksum generated for the block, which is Base64 encoded.

    ", + "location":"header", + "locationName":"x-amz-Checksum" + }, + "ChecksumAlgorithm":{ + "shape":"ChecksumAlgorithm", + "documentation":"

    The algorithm used to generate the checksum for the block, such as SHA256.

    ", + "location":"header", + "locationName":"x-amz-Checksum-Algorithm" + } + }, + "payload":"BlockData" + }, + "ListChangedBlocksRequest":{ + "type":"structure", + "required":["SecondSnapshotId"], + "members":{ + "FirstSnapshotId":{ + "shape":"SnapshotId", + "documentation":"

    The ID of the first snapshot to use for the comparison.

    The FirstSnapshotID parameter must be specified with a SecondSnapshotId parameter; otherwise, an error occurs.

    ", + "location":"querystring", + "locationName":"firstSnapshotId" + }, + "SecondSnapshotId":{ + "shape":"SnapshotId", + "documentation":"

    The ID of the second snapshot to use for the comparison.

    The SecondSnapshotId parameter must be specified with a FirstSnapshotID parameter; otherwise, an error occurs.

    ", + "location":"uri", + "locationName":"secondSnapshotId" + }, + "NextToken":{ + "shape":"PageToken", + "documentation":"

    The token to request the next page of results.

    ", + "location":"querystring", + "locationName":"pageToken" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "StartingBlockIndex":{ + "shape":"BlockIndex", + "documentation":"

    The block index from which the comparison should start.

    The list in the response will start from this block index or the next valid block index in the snapshots.

    ", + "location":"querystring", + "locationName":"startingBlockIndex" + } + } + }, + "ListChangedBlocksResponse":{ + "type":"structure", + "members":{ + "ChangedBlocks":{ + "shape":"ChangedBlocks", + "documentation":"

    An array of objects containing information about the changed blocks.

    " + }, + "ExpiryTime":{ + "shape":"TimeStamp", + "documentation":"

    The time when the BlockToken expires.

    " + }, + "VolumeSize":{ + "shape":"VolumeSize", + "documentation":"

    The size of the volume in GB.

    " + }, + "BlockSize":{ + "shape":"BlockSize", + "documentation":"

    The size of the block.

    " + }, + "NextToken":{ + "shape":"PageToken", + "documentation":"

    The token to use to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "ListSnapshotBlocksRequest":{ + "type":"structure", + "required":["SnapshotId"], + "members":{ + "SnapshotId":{ + "shape":"SnapshotId", + "documentation":"

    The ID of the snapshot from which to get block indexes and block tokens.

    ", + "location":"uri", + "locationName":"snapshotId" + }, + "NextToken":{ + "shape":"PageToken", + "documentation":"

    The token to request the next page of results.

    ", + "location":"querystring", + "locationName":"pageToken" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "StartingBlockIndex":{ + "shape":"BlockIndex", + "documentation":"

    The block index from which the list should start. The list in the response will start from this block index or the next valid block index in the snapshot.

    ", + "location":"querystring", + "locationName":"startingBlockIndex" + } + } + }, + "ListSnapshotBlocksResponse":{ + "type":"structure", + "members":{ + "Blocks":{ + "shape":"Blocks", + "documentation":"

    An array of objects containing information about the blocks.

    " + }, + "ExpiryTime":{ + "shape":"TimeStamp", + "documentation":"

    The time when the BlockToken expires.

    " + }, + "VolumeSize":{ + "shape":"VolumeSize", + "documentation":"

    The size of the volume in GB.

    " + }, + "BlockSize":{ + "shape":"BlockSize", + "documentation":"

    The size of the block.

    " + }, + "NextToken":{ + "shape":"PageToken", + "documentation":"

    The token to use to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":10000, + "min":100 + }, + "PageToken":{ + "type":"string", + "max":256, + "pattern":"^[A-Za-z0-9+/=]+$" + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The specified resource does not exist.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "SnapshotId":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^snap-[0-9a-f]+$" + }, + "TimeStamp":{"type":"timestamp"}, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"}, + "Reason":{ + "shape":"ValidationExceptionReason", + "documentation":"

    The reason for the validation exception.

    " + } + }, + "documentation":"

    The input fails to satisfy the constraints of the EBS direct APIs.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ValidationExceptionReason":{ + "type":"string", + "enum":[ + "INVALID_CUSTOMER_KEY", + "INVALID_PAGE_TOKEN", + "INVALID_BLOCK_TOKEN", + "INVALID_SNAPSHOT_ID", + "UNRELATED_SNAPSHOTS" + ] + }, + "VolumeSize":{"type":"long"} + }, + "documentation":"

    You can use the Amazon Elastic Block Store (EBS) direct APIs to directly read the data on your EBS snapshots, and identify the difference between two snapshots. You can view the details of blocks in an EBS snapshot, compare the block difference between two snapshots, and directly access the data in a snapshot. If you’re an independent software vendor (ISV) who offers backup services for EBS, the EBS direct APIs make it easier and more cost-effective to track incremental changes on your EBS volumes via EBS snapshots. This can be done without having to create new volumes from EBS snapshots.

    This API reference provides detailed information about the actions, data types, parameters, and errors of the EBS direct APIs. For more information about the elements that make up the EBS direct APIs, and examples of how to use them effectively, see Accessing the Contents of an EBS Snapshot in the Amazon Elastic Compute Cloud User Guide. For more information about the supported AWS Regions, endpoints, and service quotas for the EBS direct APIs, see Amazon Elastic Block Store Endpoints and Quotas in the AWS General Reference.

    " +} diff --git a/services/ec2/build.properties b/services/ec2/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/ec2/build.properties +++ b/services/ec2/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/ec2/pom.xml b/services/ec2/pom.xml index ecab67b20c97..9220edd2b98f 100644 --- a/services/ec2/pom.xml +++ b/services/ec2/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + elasticinference + AWS Java SDK :: Services :: Elastic Inference + The AWS Java SDK for Elastic Inference module holds the client classes that are used for + communicating with Elastic Inference. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.elasticinference + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/elasticinference/src/main/resources/codegen-resources/paginators-1.json b/services/elasticinference/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..5677bd8e4a2d --- /dev/null +++ b/services/elasticinference/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,4 @@ +{ + "pagination": { + } +} diff --git a/services/elasticinference/src/main/resources/codegen-resources/service-2.json b/services/elasticinference/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..b01d8ea368df --- /dev/null +++ b/services/elasticinference/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,191 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2017-07-25", + "endpointPrefix":"elastic-inference", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"Amazon Elastic Inference", + "serviceFullName":"Amazon Elastic Inference", + "serviceId":"Elastic Inference", + "signatureVersion":"v4", + "signingName":"elastic-inference", + "uid":"elastic-inference-2017-07-25" + }, + "operations":{ + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"GET", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResult"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"Returns all tags of an Elastic Inference Accelerator." + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResult"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"Adds the specified tag(s) to an Elastic Inference Accelerator." + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"DELETE", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResult"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"} + ], + "documentation":"Removes the specified tag(s) from an Elastic Inference Accelerator." + } + }, + "shapes":{ + "BadRequestException":{ + "type":"structure", + "members":{ + "message":{"shape":"String"} + }, + "documentation":"Raised when a malformed input has been provided to the API.", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InternalServerException":{ + "type":"structure", + "members":{ + "message":{"shape":"String"} + }, + "documentation":"Raised when an unexpected error occurred during request processing.", + "error":{"httpStatusCode":500}, + "exception":true + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["resourceArn"], + "members":{ + "resourceArn":{ + "shape":"ResourceARN", + "documentation":"The ARN of the Elastic Inference Accelerator to list the tags for.", + "location":"uri", + "locationName":"resourceArn" + } + } + }, + "ListTagsForResourceResult":{ + "type":"structure", + "members":{ + "tags":{ + "shape":"TagMap", + "documentation":"The tags of the Elastic Inference Accelerator." + } + } + }, + "ResourceARN":{ + "type":"string", + "max":1011, + "min":1 + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"String"} + }, + "documentation":"Raised when the requested resource cannot be found.", + "error":{"httpStatusCode":404}, + "exception":true + }, + "String":{"type":"string"}, + "TagKey":{ + "type":"string", + "max":128, + "min":1 + }, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"}, + "max":50, + "min":1 + }, + "TagMap":{ + "type":"map", + "key":{"shape":"TagKey"}, + "value":{"shape":"TagValue"}, + "max":50, + "min":1 + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tags" + ], + "members":{ + "resourceArn":{ + "shape":"ResourceARN", + "documentation":"The ARN of the Elastic Inference Accelerator to tag.", + "location":"uri", + "locationName":"resourceArn" + }, + "tags":{ + "shape":"TagMap", + "documentation":"The tags to add to the Elastic Inference Accelerator." + } + } + }, + "TagResourceResult":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{ + "type":"string", + "max":256 + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tagKeys" + ], + "members":{ + "resourceArn":{ + "shape":"ResourceARN", + "documentation":"The ARN of the Elastic Inference Accelerator to untag.", + "location":"uri", + "locationName":"resourceArn" + }, + "tagKeys":{ + "shape":"TagKeyList", + "documentation":"The list of tags to remove from the Elastic Inference Accelerator.", + "location":"querystring", + "locationName":"tagKeys" + } + } + }, + "UntagResourceResult":{ + "type":"structure", + "members":{ + } + } + }, + "documentation":"Elastic Inference public APIs." +} diff --git a/services/elasticloadbalancing/build.properties b/services/elasticloadbalancing/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/elasticloadbalancing/build.properties +++ b/services/elasticloadbalancing/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/elasticloadbalancing/pom.xml b/services/elasticloadbalancing/pom.xml index 7595031aeb58..61d2f5301b79 100644 --- a/services/elasticloadbalancing/pom.xml +++ b/services/elasticloadbalancing/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + frauddetector + AWS Java SDK :: Services :: FraudDetector + The AWS Java SDK for FraudDetector module holds the client classes that are used for + communicating with FraudDetector. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.frauddetector + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/frauddetector/src/main/resources/codegen-resources/paginators-1.json b/services/frauddetector/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..b66f8459ad2e --- /dev/null +++ b/services/frauddetector/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,39 @@ +{ + "pagination": { + "DescribeModelVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "GetDetectors": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "GetExternalModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "GetModels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "GetOutcomes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "GetRules": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "GetVariables": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/services/frauddetector/src/main/resources/codegen-resources/service-2.json b/services/frauddetector/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..225f69e1a344 --- /dev/null +++ b/services/frauddetector/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,2338 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-11-15", + "endpointPrefix":"frauddetector", + "jsonVersion":"1.1", + "protocol":"json", + "serviceFullName":"Amazon Fraud Detector", + "serviceId":"FraudDetector", + "signatureVersion":"v4", + "targetPrefix":"AWSHawksNestServiceFacade", + "uid":"frauddetector-2019-11-15" + }, + "operations":{ + "BatchCreateVariable":{ + "name":"BatchCreateVariable", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"BatchCreateVariableRequest"}, + "output":{"shape":"BatchCreateVariableResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates a batch of variables.

    " + }, + "BatchGetVariable":{ + "name":"BatchGetVariable", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"BatchGetVariableRequest"}, + "output":{"shape":"BatchGetVariableResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets a batch of variables.

    " + }, + "CreateDetectorVersion":{ + "name":"CreateDetectorVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateDetectorVersionRequest"}, + "output":{"shape":"CreateDetectorVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates a detector version. The detector version starts in a DRAFT status.

    " + }, + "CreateModelVersion":{ + "name":"CreateModelVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateModelVersionRequest"}, + "output":{"shape":"CreateModelVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates a version of the model using the specified model type.

    " + }, + "CreateRule":{ + "name":"CreateRule", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateRuleRequest"}, + "output":{"shape":"CreateRuleResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates a rule for use with the specified detector.

    " + }, + "CreateVariable":{ + "name":"CreateVariable", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateVariableRequest"}, + "output":{"shape":"CreateVariableResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates a variable.

    " + }, + "DeleteDetectorVersion":{ + "name":"DeleteDetectorVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteDetectorVersionRequest"}, + "output":{"shape":"DeleteDetectorVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Deletes the detector version.

    " + }, + "DeleteEvent":{ + "name":"DeleteEvent", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteEventRequest"}, + "output":{"shape":"DeleteEventResult"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Deletes the specified event.

    " + }, + "DescribeDetector":{ + "name":"DescribeDetector", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeDetectorRequest"}, + "output":{"shape":"DescribeDetectorResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets all versions for a specified detector.

    " + }, + "DescribeModelVersions":{ + "name":"DescribeModelVersions", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeModelVersionsRequest"}, + "output":{"shape":"DescribeModelVersionsResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets all of the model versions for the specified model type or for the specified model type and model ID. You can also get details for a single, specified model version.

    " + }, + "GetDetectorVersion":{ + "name":"GetDetectorVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetDetectorVersionRequest"}, + "output":{"shape":"GetDetectorVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets a particular detector version.

    " + }, + "GetDetectors":{ + "name":"GetDetectors", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetDetectorsRequest"}, + "output":{"shape":"GetDetectorsResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets all of detectors. This is a paginated API. If you provide a null maxSizePerPage, this actions retrieves a maximum of 10 records per page. If you provide a maxSizePerPage, the value must be between 5 and 10. To get the next page results, provide the pagination token from the GetEventTypesResponse as part of your request. A null pagination token fetches the records from the beginning.

    " + }, + "GetExternalModels":{ + "name":"GetExternalModels", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetExternalModelsRequest"}, + "output":{"shape":"GetExternalModelsResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets the details for one or more Amazon SageMaker models that have been imported into the service. This is a paginated API. If you provide a null maxSizePerPage, this actions retrieves a maximum of 10 records per page. If you provide a maxSizePerPage, the value must be between 5 and 10. To get the next page results, provide the pagination token from the GetExternalModelsResult as part of your request. A null pagination token fetches the records from the beginning.

    " + }, + "GetModelVersion":{ + "name":"GetModelVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetModelVersionRequest"}, + "output":{"shape":"GetModelVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets a model version.

    " + }, + "GetModels":{ + "name":"GetModels", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetModelsRequest"}, + "output":{"shape":"GetModelsResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets all of the models for the AWS account, or the specified model type, or gets a single model for the specified model type, model ID combination.

    " + }, + "GetOutcomes":{ + "name":"GetOutcomes", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetOutcomesRequest"}, + "output":{"shape":"GetOutcomesResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets one or more outcomes. This is a paginated API. If you provide a null maxSizePerPage, this actions retrieves a maximum of 10 records per page. If you provide a maxSizePerPage, the value must be between 50 and 100. To get the next page results, provide the pagination token from the GetOutcomesResult as part of your request. A null pagination token fetches the records from the beginning.

    " + }, + "GetPrediction":{ + "name":"GetPrediction", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetPredictionRequest"}, + "output":{"shape":"GetPredictionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Evaluates an event against a detector version. If a version ID is not provided, the detector’s (ACTIVE) version is used.

    " + }, + "GetRules":{ + "name":"GetRules", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetRulesRequest"}, + "output":{"shape":"GetRulesResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets all rules available for the specified detector.

    " + }, + "GetVariables":{ + "name":"GetVariables", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetVariablesRequest"}, + "output":{"shape":"GetVariablesResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Gets all of the variables or the specific variable. This is a paginated API. Providing null maxSizePerPage results in retrieving maximum of 100 records per page. If you provide maxSizePerPage the value must be between 50 and 100. To get the next page result, a provide a pagination token from GetVariablesResult as part of your request. Null pagination token fetches the records from the beginning.

    " + }, + "PutDetector":{ + "name":"PutDetector", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutDetectorRequest"}, + "output":{"shape":"PutDetectorResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates or updates a detector.

    " + }, + "PutExternalModel":{ + "name":"PutExternalModel", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutExternalModelRequest"}, + "output":{"shape":"PutExternalModelResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates or updates an Amazon SageMaker model endpoint. You can also use this action to update the configuration of the model endpoint, including the IAM role and/or the mapped variables.

    " + }, + "PutModel":{ + "name":"PutModel", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutModelRequest"}, + "output":{"shape":"PutModelResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates or updates a model.

    " + }, + "PutOutcome":{ + "name":"PutOutcome", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutOutcomeRequest"}, + "output":{"shape":"PutOutcomeResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Creates or updates an outcome.

    " + }, + "UpdateDetectorVersion":{ + "name":"UpdateDetectorVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateDetectorVersionRequest"}, + "output":{"shape":"UpdateDetectorVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates a detector version. The detector version attributes that you can update include models, external model endpoints, rules, and description. You can only update a DRAFT detector version.

    " + }, + "UpdateDetectorVersionMetadata":{ + "name":"UpdateDetectorVersionMetadata", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateDetectorVersionMetadataRequest"}, + "output":{"shape":"UpdateDetectorVersionMetadataResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates the detector version's description. You can update the metadata for any detector version (DRAFT, ACTIVE, or INACTIVE).

    " + }, + "UpdateDetectorVersionStatus":{ + "name":"UpdateDetectorVersionStatus", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateDetectorVersionStatusRequest"}, + "output":{"shape":"UpdateDetectorVersionStatusResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates the detector version’s status. You can perform the following promotions or demotions using UpdateDetectorVersionStatus: DRAFT to ACTIVE, ACTIVE to INACTIVE, and INACTIVE to ACTIVE.

    " + }, + "UpdateModelVersion":{ + "name":"UpdateModelVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateModelVersionRequest"}, + "output":{"shape":"UpdateModelVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates a model version. You can update the description and status attributes using this action. You can perform the following status updates:

    1. Change the TRAINING_COMPLETE status to ACTIVE

    2. Change ACTIVE back to TRAINING_COMPLETE

    " + }, + "UpdateRuleMetadata":{ + "name":"UpdateRuleMetadata", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateRuleMetadataRequest"}, + "output":{"shape":"UpdateRuleMetadataResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates a rule's metadata.

    " + }, + "UpdateRuleVersion":{ + "name":"UpdateRuleVersion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateRuleVersionRequest"}, + "output":{"shape":"UpdateRuleVersionResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates a rule version resulting in a new rule version.

    " + }, + "UpdateVariable":{ + "name":"UpdateVariable", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateVariableRequest"}, + "output":{"shape":"UpdateVariableResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InternalServerException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Updates a variable.

    " + } + }, + "shapes":{ + "BatchCreateVariableError":{ + "type":"structure", + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The name.

    " + }, + "code":{ + "shape":"integer", + "documentation":"

    The error code.

    " + }, + "message":{ + "shape":"string", + "documentation":"

    The error message.

    " + } + }, + "documentation":"

    Provides the error of the batch create variable API.

    " + }, + "BatchCreateVariableErrorList":{ + "type":"list", + "member":{"shape":"BatchCreateVariableError"} + }, + "BatchCreateVariableRequest":{ + "type":"structure", + "required":["variableEntries"], + "members":{ + "variableEntries":{ + "shape":"VariableEntryList", + "documentation":"

    The list of variables for the batch create variable request.

    " + } + } + }, + "BatchCreateVariableResult":{ + "type":"structure", + "members":{ + "errors":{ + "shape":"BatchCreateVariableErrorList", + "documentation":"

    Provides the errors for the BatchCreateVariable request.

    " + } + } + }, + "BatchGetVariableError":{ + "type":"structure", + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The error name.

    " + }, + "code":{ + "shape":"integer", + "documentation":"

    The error code.

    " + }, + "message":{ + "shape":"string", + "documentation":"

    The error message.

    " + } + }, + "documentation":"

    Provides the error of the batch get variable API.

    " + }, + "BatchGetVariableErrorList":{ + "type":"list", + "member":{"shape":"BatchGetVariableError"} + }, + "BatchGetVariableRequest":{ + "type":"structure", + "required":["names"], + "members":{ + "names":{ + "shape":"NameList", + "documentation":"

    The list of variable names to get.

    " + } + } + }, + "BatchGetVariableResult":{ + "type":"structure", + "members":{ + "variables":{ + "shape":"VariableList", + "documentation":"

    The returned variables.

    " + }, + "errors":{ + "shape":"BatchGetVariableErrorList", + "documentation":"

    The errors from the request.

    " + } + } + }, + "CreateDetectorVersionRequest":{ + "type":"structure", + "required":[ + "detectorId", + "rules" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The ID of the detector under which you want to create a new version.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The description of the detector version.

    " + }, + "externalModelEndpoints":{ + "shape":"ListOfStrings", + "documentation":"

    The Amazon Sagemaker model endpoints to include in the detector version.

    " + }, + "rules":{ + "shape":"RuleList", + "documentation":"

    The rules to include in the detector version.

    " + }, + "modelVersions":{ + "shape":"ListOfModelVersions", + "documentation":"

    The model versions to include in the detector version.

    " + } + } + }, + "CreateDetectorVersionResult":{ + "type":"structure", + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The ID for the created version's parent detector.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The ID for the created detector.

    " + }, + "status":{ + "shape":"DetectorVersionStatus", + "documentation":"

    The status of the detector version.

    " + } + } + }, + "CreateModelVersionRequest":{ + "type":"structure", + "required":[ + "modelId", + "modelType" + ], + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The model version description.

    " + } + } + }, + "CreateModelVersionResult":{ + "type":"structure", + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The version of the model.

    " + }, + "status":{ + "shape":"string", + "documentation":"

    The model version status.

    " + } + } + }, + "CreateRuleRequest":{ + "type":"structure", + "required":[ + "ruleId", + "detectorId", + "expression", + "language", + "outcomes" + ], + "members":{ + "ruleId":{ + "shape":"identifier", + "documentation":"

    The rule ID.

    " + }, + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID for the rule's parent detector.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The rule description.

    " + }, + "expression":{ + "shape":"ruleExpression", + "documentation":"

    The rule expression.

    " + }, + "language":{ + "shape":"Language", + "documentation":"

    The language of the rule.

    " + }, + "outcomes":{ + "shape":"NonEmptyListOfStrings", + "documentation":"

    The outcome or outcomes returned when the rule expression matches.

    " + } + } + }, + "CreateRuleResult":{ + "type":"structure", + "members":{ + "rule":{ + "shape":"Rule", + "documentation":"

    The created rule.

    " + } + } + }, + "CreateVariableRequest":{ + "type":"structure", + "required":[ + "name", + "dataType", + "dataSource", + "defaultValue" + ], + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The name of the variable.

    " + }, + "dataType":{ + "shape":"DataType", + "documentation":"

    The data type.

    " + }, + "dataSource":{ + "shape":"DataSource", + "documentation":"

    The source of the data.

    " + }, + "defaultValue":{ + "shape":"string", + "documentation":"

    The default value for the variable when no value is received.

    " + }, + "description":{ + "shape":"string", + "documentation":"

    The description.

    " + }, + "variableType":{ + "shape":"string", + "documentation":"

    The variable type.

    " + } + } + }, + "CreateVariableResult":{ + "type":"structure", + "members":{ + } + }, + "CsvIndexToVariableMap":{ + "type":"map", + "key":{"shape":"string"}, + "value":{"shape":"string"} + }, + "DataSource":{ + "type":"string", + "enum":[ + "EVENT", + "MODEL_SCORE", + "EXTERNAL_MODEL_SCORE" + ] + }, + "DataType":{ + "type":"string", + "enum":[ + "STRING", + "INTEGER", + "FLOAT", + "BOOLEAN" + ] + }, + "DeleteDetectorVersionRequest":{ + "type":"structure", + "required":[ + "detectorId", + "detectorVersionId" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The ID of the parent detector for the detector version to delete.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The ID of the detector version to delete.

    " + } + } + }, + "DeleteDetectorVersionResult":{ + "type":"structure", + "members":{ + } + }, + "DeleteEventRequest":{ + "type":"structure", + "required":["eventId"], + "members":{ + "eventId":{ + "shape":"string", + "documentation":"

    The ID of the event to delete.

    " + } + } + }, + "DeleteEventResult":{ + "type":"structure", + "members":{ + } + }, + "DescribeDetectorRequest":{ + "type":"structure", + "required":["detectorId"], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next token from the previous response.

    " + }, + "maxResults":{ + "shape":"DetectorVersionMaxResults", + "documentation":"

    The maximum number of results to return for the request.

    " + } + } + }, + "DescribeDetectorResult":{ + "type":"structure", + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "detectorVersionSummaries":{ + "shape":"DetectorVersionSummaryList", + "documentation":"

    The status and description for each detector version.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next token to be used for subsequent requests.

    " + } + } + }, + "DescribeModelVersionsRequest":{ + "type":"structure", + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The model version.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next token from the previous results.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    " + } + } + }, + "DescribeModelVersionsResult":{ + "type":"structure", + "members":{ + "modelVersionDetails":{ + "shape":"ModelVersionDetailList", + "documentation":"

    The model version details.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next token.

    " + } + } + }, + "Detector":{ + "type":"structure", + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The detector description.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    Timestamp of when the detector was last updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    Timestamp of when the detector was created.

    " + } + }, + "documentation":"

    The detector.

    " + }, + "DetectorList":{ + "type":"list", + "member":{"shape":"Detector"} + }, + "DetectorVersionMaxResults":{ + "type":"integer", + "box":true, + "max":2500, + "min":1000 + }, + "DetectorVersionStatus":{ + "type":"string", + "enum":[ + "DRAFT", + "ACTIVE", + "INACTIVE" + ] + }, + "DetectorVersionSummary":{ + "type":"structure", + "members":{ + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The detector version ID.

    " + }, + "status":{ + "shape":"DetectorVersionStatus", + "documentation":"

    The detector version status.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The detector version description.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    Timestamp of when the detector version was last updated.

    " + } + }, + "documentation":"

    The summary of the detector version.

    " + }, + "DetectorVersionSummaryList":{ + "type":"list", + "member":{"shape":"DetectorVersionSummary"} + }, + "DetectorsMaxResults":{ + "type":"integer", + "box":true, + "max":10, + "min":5 + }, + "EventAttributeMap":{ + "type":"map", + "key":{"shape":"attributeKey"}, + "value":{"shape":"attributeValue"} + }, + "ExternalModel":{ + "type":"structure", + "members":{ + "modelEndpoint":{ + "shape":"string", + "documentation":"

    The Amazon SageMaker model endpoints.

    " + }, + "modelSource":{ + "shape":"ModelSource", + "documentation":"

    The source of the model.

    " + }, + "role":{ + "shape":"Role", + "documentation":"

    The role used to invoke the model.

    " + }, + "inputConfiguration":{ + "shape":"ModelInputConfiguration", + "documentation":"

    The input configuration.

    " + }, + "outputConfiguration":{ + "shape":"ModelOutputConfiguration", + "documentation":"

    The output configuration.

    " + }, + "modelEndpointStatus":{ + "shape":"ModelEndpointStatus", + "documentation":"

    The Amazon Fraud Detector status for the external model endpoint

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    Timestamp of when the model was last updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    Timestamp of when the model was last created.

    " + } + }, + "documentation":"

    The Amazon SageMaker model.

    " + }, + "ExternalModelEndpointDataBlobMap":{ + "type":"map", + "key":{"shape":"string"}, + "value":{"shape":"ModelEndpointDataBlob"}, + "sensitive":true + }, + "ExternalModelList":{ + "type":"list", + "member":{"shape":"ExternalModel"} + }, + "ExternalModelsMaxResults":{ + "type":"integer", + "box":true, + "max":10, + "min":5 + }, + "GetDetectorVersionRequest":{ + "type":"structure", + "required":[ + "detectorId", + "detectorVersionId" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The detector version ID.

    " + } + } + }, + "GetDetectorVersionResult":{ + "type":"structure", + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The detector version ID.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The detector version description.

    " + }, + "externalModelEndpoints":{ + "shape":"ListOfStrings", + "documentation":"

    The Amazon SageMaker model endpoints included in the detector version.

    " + }, + "modelVersions":{ + "shape":"ListOfModelVersions", + "documentation":"

    The model versions included in the detector version.

    " + }, + "rules":{ + "shape":"RuleList", + "documentation":"

    The rules included in the detector version.

    " + }, + "status":{ + "shape":"DetectorVersionStatus", + "documentation":"

    The status of the detector version.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    The timestamp when the detector version was last updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    The timestamp when the detector version was created.

    " + } + } + }, + "GetDetectorsRequest":{ + "type":"structure", + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next token for the subsequent request.

    " + }, + "maxResults":{ + "shape":"DetectorsMaxResults", + "documentation":"

    The maximum number of objects to return for the request.

    " + } + } + }, + "GetDetectorsResult":{ + "type":"structure", + "members":{ + "detectors":{ + "shape":"DetectorList", + "documentation":"

    The detectors.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token.

    " + } + } + }, + "GetExternalModelsRequest":{ + "type":"structure", + "members":{ + "modelEndpoint":{ + "shape":"string", + "documentation":"

    The Amazon SageMaker model endpoint.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token for the request.

    " + }, + "maxResults":{ + "shape":"ExternalModelsMaxResults", + "documentation":"

    The maximum number of objects to return for the request.

    " + } + } + }, + "GetExternalModelsResult":{ + "type":"structure", + "members":{ + "externalModels":{ + "shape":"ExternalModelList", + "documentation":"

    Gets the Amazon SageMaker models.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token to be used in subsequent requests.

    " + } + } + }, + "GetModelVersionRequest":{ + "type":"structure", + "required":[ + "modelId", + "modelType", + "modelVersionNumber" + ], + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The model version.

    " + } + } + }, + "GetModelVersionResult":{ + "type":"structure", + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The model version.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The model version description.

    " + }, + "status":{ + "shape":"string", + "documentation":"

    The model version status.

    " + } + } + }, + "GetModelsRequest":{ + "type":"structure", + "members":{ + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next token for the request.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum results to return for the request.

    " + } + } + }, + "GetModelsResult":{ + "type":"structure", + "members":{ + "nextToken":{ + "shape":"string", + "documentation":"

    The next token for subsequent requests.

    " + }, + "models":{ + "shape":"ModelList", + "documentation":"

    The returned models.

    " + } + } + }, + "GetOutcomesRequest":{ + "type":"structure", + "members":{ + "name":{ + "shape":"identifier", + "documentation":"

    The name of the outcome or outcomes to get.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token for the request.

    " + }, + "maxResults":{ + "shape":"OutcomesMaxResults", + "documentation":"

    The maximum number of objects to return for the request.

    " + } + } + }, + "GetOutcomesResult":{ + "type":"structure", + "members":{ + "outcomes":{ + "shape":"OutcomeList", + "documentation":"

    The outcomes.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token for subsequent requests.

    " + } + } + }, + "GetPredictionRequest":{ + "type":"structure", + "required":[ + "detectorId", + "eventId" + ], + "members":{ + "detectorId":{ + "shape":"string", + "documentation":"

    The detector ID.

    " + }, + "detectorVersionId":{ + "shape":"string", + "documentation":"

    The detector version ID.

    " + }, + "eventId":{ + "shape":"string", + "documentation":"

    The unique ID used to identify the event.

    " + }, + "eventAttributes":{ + "shape":"EventAttributeMap", + "documentation":"

    Names of variables you defined in Amazon Fraud Detector to represent event data elements and their corresponding values for the event you are sending for evaluation.

    " + }, + "externalModelEndpointDataBlobs":{ + "shape":"ExternalModelEndpointDataBlobMap", + "documentation":"

    The Amazon SageMaker model endpoint input data blobs.

    " + } + } + }, + "GetPredictionResult":{ + "type":"structure", + "members":{ + "outcomes":{ + "shape":"ListOfStrings", + "documentation":"

    The prediction outcomes.

    " + }, + "modelScores":{ + "shape":"ListOfModelScores", + "documentation":"

    The model scores for models used in the detector version.

    " + } + } + }, + "GetRulesRequest":{ + "type":"structure", + "required":["detectorId"], + "members":{ + "ruleId":{ + "shape":"identifier", + "documentation":"

    The rule ID.

    " + }, + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "ruleVersion":{ + "shape":"nonEmptyString", + "documentation":"

    The rule version.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token.

    " + }, + "maxResults":{ + "shape":"RulesMaxResults", + "documentation":"

    The maximum number of rules to return for the request.

    " + } + } + }, + "GetRulesResult":{ + "type":"structure", + "members":{ + "ruleDetails":{ + "shape":"RuleDetailList", + "documentation":"

    The details of the requested rule.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token to be used in subsequent requests.

    " + } + } + }, + "GetVariablesRequest":{ + "type":"structure", + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The name of the variable.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token of the get variable request.

    " + }, + "maxResults":{ + "shape":"VariablesMaxResults", + "documentation":"

    The max size per page determined for the get variable request.

    " + } + } + }, + "GetVariablesResult":{ + "type":"structure", + "members":{ + "variables":{ + "shape":"VariableList", + "documentation":"

    The names of the variables returned.

    " + }, + "nextToken":{ + "shape":"string", + "documentation":"

    The next page token to be used in subsequent requests.

    " + } + } + }, + "InternalServerException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"string"} + }, + "documentation":"

    An exception indicating an internal server error.

    ", + "exception":true, + "fault":true + }, + "IsOpaque":{"type":"boolean"}, + "JsonKeyToVariableMap":{ + "type":"map", + "key":{"shape":"string"}, + "value":{"shape":"string"} + }, + "LabelMapper":{ + "type":"map", + "key":{"shape":"string"}, + "value":{"shape":"ListOfStrings"} + }, + "LabelSchema":{ + "type":"structure", + "required":[ + "labelKey", + "labelMapper" + ], + "members":{ + "labelKey":{ + "shape":"string", + "documentation":"

    The label key.

    " + }, + "labelMapper":{ + "shape":"LabelMapper", + "documentation":"

    The label mapper maps the Amazon Fraud Detector supported label to the appropriate source labels. For example, if \"FRAUD\" and \"LEGIT\" are Amazon Fraud Detector supported labels, this mapper could be: {\"FRAUD\" => [\"0\"], \"LEGIT\" => [\"1\"]} or {\"FRAUD\" => [\"false\"], \"LEGIT\" => [\"true\"]} or {\"FRAUD\" => [\"fraud\", \"abuse\"], \"LEGIT\" => [\"legit\", \"safe\"]}. The value part of the mapper is a list, because you may have multiple variants for a single Amazon Fraud Detector label.

    " + } + }, + "documentation":"

    The label schema.

    " + }, + "Language":{ + "type":"string", + "enum":["DETECTORPL"] + }, + "ListOfModelScores":{ + "type":"list", + "member":{"shape":"ModelScores"} + }, + "ListOfModelVersions":{ + "type":"list", + "member":{"shape":"ModelVersion"} + }, + "ListOfStrings":{ + "type":"list", + "member":{"shape":"string"} + }, + "MaxResults":{ + "type":"integer", + "box":true, + "max":10, + "min":1 + }, + "MetricsMap":{ + "type":"map", + "key":{"shape":"string"}, + "value":{"shape":"string"} + }, + "Model":{ + "type":"structure", + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The model description.

    " + }, + "trainingDataSource":{ + "shape":"TrainingDataSource", + "documentation":"

    The model training data source in Amazon S3.

    " + }, + "modelVariables":{ + "shape":"ModelVariablesList", + "documentation":"

    The model input variables.

    " + }, + "labelSchema":{ + "shape":"LabelSchema", + "documentation":"

    The model label schema.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    Timestamp of last time the model was updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    Timestamp of when the model was created.

    " + } + }, + "documentation":"

    The model.

    " + }, + "ModelEndpointDataBlob":{ + "type":"structure", + "members":{ + "byteBuffer":{ + "shape":"blob", + "documentation":"

    The byte buffer of the Amazon SageMaker model endpoint input data blob.

    " + }, + "contentType":{ + "shape":"contentType", + "documentation":"

    The content type of the Amazon SageMaker model endpoint input data blob.

    " + } + }, + "documentation":"

    A pre-formed Amazon SageMaker model input you can include if your detector version includes an imported Amazon SageMaker model endpoint with pass-through input configuration.

    " + }, + "ModelEndpointStatus":{ + "type":"string", + "enum":[ + "ASSOCIATED", + "DISSOCIATED" + ] + }, + "ModelInputConfiguration":{ + "type":"structure", + "required":["isOpaque"], + "members":{ + "format":{ + "shape":"ModelInputDataFormat", + "documentation":"

    The format of the model input configuration. The format differs depending on if it is passed through to SageMaker or constructed by Amazon Fraud Detector.

    " + }, + "isOpaque":{ + "shape":"IsOpaque", + "documentation":"

    For an opaque-model, the input to the model will be a ByteBuffer blob provided in the getPrediction request, and will be passed to SageMaker as-is. For non-opaque models, the input will be constructed by Amazon Fraud Detector based on the model-configuration.

    " + }, + "jsonInputTemplate":{ + "shape":"string", + "documentation":"

    Template for constructing the JSON input-data sent to SageMaker. At event-evaluation, the placeholders for variable names in the template will be replaced with the variable values before being sent to SageMaker.

    " + }, + "csvInputTemplate":{ + "shape":"string", + "documentation":"

    Template for constructing the CSV input-data sent to SageMaker. At event-evaluation, the placeholders for variable-names in the template will be replaced with the variable values before being sent to SageMaker.

    " + } + }, + "documentation":"

    The model input configuration.

    " + }, + "ModelInputDataFormat":{ + "type":"string", + "enum":[ + "TEXT_CSV", + "APPLICATION_JSON" + ] + }, + "ModelList":{ + "type":"list", + "member":{"shape":"Model"} + }, + "ModelOutputConfiguration":{ + "type":"structure", + "required":["format"], + "members":{ + "format":{ + "shape":"ModelOutputDataFormat", + "documentation":"

    The format of the model output configuration.

    " + }, + "jsonKeyToVariableMap":{ + "shape":"JsonKeyToVariableMap", + "documentation":"

    A map of JSON keys in response from SageMaker to the Amazon Fraud Detector variables.

    " + }, + "csvIndexToVariableMap":{ + "shape":"CsvIndexToVariableMap", + "documentation":"

    A map of CSV index values in the SageMaker response to the Amazon Fraud Detector variables.

    " + } + }, + "documentation":"

    Provides the model output configuration.

    " + }, + "ModelOutputDataFormat":{ + "type":"string", + "enum":[ + "TEXT_CSV", + "APPLICATION_JSONLINES" + ] + }, + "ModelPredictionMap":{ + "type":"map", + "key":{"shape":"string"}, + "value":{"shape":"float"} + }, + "ModelScores":{ + "type":"structure", + "members":{ + "modelVersion":{ + "shape":"ModelVersion", + "documentation":"

    The model version.

    " + }, + "scores":{ + "shape":"ModelPredictionMap", + "documentation":"

    The model's fraud prediction scores.

    " + } + }, + "documentation":"

    The fraud prediction scores.

    " + }, + "ModelSource":{ + "type":"string", + "enum":["SAGEMAKER"] + }, + "ModelTypeEnum":{ + "type":"string", + "enum":["ONLINE_FRAUD_INSIGHTS"] + }, + "ModelVariable":{ + "type":"structure", + "required":["name"], + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The model variable's name.>

    " + }, + "index":{ + "shape":"ModelVariableIndex", + "documentation":"

    The model variable's index.>

    " + } + }, + "documentation":"

    The model variable.>

    " + }, + "ModelVariableIndex":{"type":"integer"}, + "ModelVariablesList":{ + "type":"list", + "member":{"shape":"ModelVariable"} + }, + "ModelVersion":{ + "type":"structure", + "required":[ + "modelId", + "modelType", + "modelVersionNumber" + ], + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The parent model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The model version.

    " + } + }, + "documentation":"

    The model version.

    " + }, + "ModelVersionDetail":{ + "type":"structure", + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The model version.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The model description.

    " + }, + "status":{ + "shape":"string", + "documentation":"

    The model status.

    " + }, + "trainingDataSource":{ + "shape":"TrainingDataSource", + "documentation":"

    The model training data source.

    " + }, + "modelVariables":{ + "shape":"ModelVariablesList", + "documentation":"

    The model variables.

    " + }, + "labelSchema":{ + "shape":"LabelSchema", + "documentation":"

    The model label schema.

    " + }, + "validationMetrics":{ + "shape":"MetricsMap", + "documentation":"

    The model validation metrics.

    " + }, + "trainingMetrics":{ + "shape":"MetricsMap", + "documentation":"

    The model training metrics.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    The timestamp when the model was last updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    The timestamp when the model was created.

    " + } + }, + "documentation":"

    Provides the model version details.

    " + }, + "ModelVersionDetailList":{ + "type":"list", + "member":{"shape":"ModelVersionDetail"} + }, + "ModelVersionStatus":{ + "type":"string", + "enum":[ + "TRAINING_IN_PROGRESS", + "TRAINING_COMPLETE", + "ACTIVATE_REQUESTED", + "ACTIVATE_IN_PROGRESS", + "ACTIVE", + "INACTIVATE_IN_PROGRESS", + "INACTIVE", + "ERROR" + ] + }, + "NameList":{ + "type":"list", + "member":{"shape":"string"}, + "max":100, + "min":1 + }, + "NonEmptyListOfStrings":{ + "type":"list", + "member":{"shape":"string"}, + "min":1 + }, + "Outcome":{ + "type":"structure", + "members":{ + "name":{ + "shape":"identifier", + "documentation":"

    The outcome name.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The outcome description.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    The timestamp when the outcome was last updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    The timestamp when the outcome was created.

    " + } + }, + "documentation":"

    The outcome.

    " + }, + "OutcomeList":{ + "type":"list", + "member":{"shape":"Outcome"} + }, + "OutcomesMaxResults":{ + "type":"integer", + "box":true, + "max":100, + "min":50 + }, + "PutDetectorRequest":{ + "type":"structure", + "required":["detectorId"], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The description of the detector.

    " + } + } + }, + "PutDetectorResult":{ + "type":"structure", + "members":{ + } + }, + "PutExternalModelRequest":{ + "type":"structure", + "required":[ + "modelEndpoint", + "modelSource", + "role", + "inputConfiguration", + "outputConfiguration", + "modelEndpointStatus" + ], + "members":{ + "modelEndpoint":{ + "shape":"string", + "documentation":"

    The model endpoints name.

    " + }, + "modelSource":{ + "shape":"ModelSource", + "documentation":"

    The source of the model.

    " + }, + "role":{ + "shape":"Role", + "documentation":"

    The IAM role used to invoke the model endpoint.

    " + }, + "inputConfiguration":{ + "shape":"ModelInputConfiguration", + "documentation":"

    The model endpoint input configuration.

    " + }, + "outputConfiguration":{ + "shape":"ModelOutputConfiguration", + "documentation":"

    The model endpoint output configuration.

    " + }, + "modelEndpointStatus":{ + "shape":"ModelEndpointStatus", + "documentation":"

    The model endpoint’s status in Amazon Fraud Detector.

    " + } + } + }, + "PutExternalModelResult":{ + "type":"structure", + "members":{ + } + }, + "PutModelRequest":{ + "type":"structure", + "required":[ + "modelId", + "modelType", + "trainingDataSource", + "modelVariables", + "labelSchema" + ], + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The model description.

    " + }, + "trainingDataSource":{ + "shape":"TrainingDataSource", + "documentation":"

    The training data source location in Amazon S3.

    " + }, + "modelVariables":{ + "shape":"ModelVariablesList", + "documentation":"

    The model input variables.

    " + }, + "labelSchema":{ + "shape":"LabelSchema", + "documentation":"

    The label schema.

    " + } + } + }, + "PutModelResult":{ + "type":"structure", + "members":{ + } + }, + "PutOutcomeRequest":{ + "type":"structure", + "required":["name"], + "members":{ + "name":{ + "shape":"identifier", + "documentation":"

    The name of the outcome.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The outcome description.

    " + } + } + }, + "PutOutcomeResult":{ + "type":"structure", + "members":{ + } + }, + "ResourceNotFoundException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"string"} + }, + "documentation":"

    An exception indicating the specified resource was not found.

    ", + "exception":true + }, + "Role":{ + "type":"structure", + "required":[ + "arn", + "name" + ], + "members":{ + "arn":{ + "shape":"string", + "documentation":"

    The role ARN.

    " + }, + "name":{ + "shape":"string", + "documentation":"

    The role name.

    " + } + }, + "documentation":"

    The role used to invoke external model endpoints.

    " + }, + "Rule":{ + "type":"structure", + "required":[ + "detectorId", + "ruleId", + "ruleVersion" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector for which the rule is associated.

    " + }, + "ruleId":{ + "shape":"identifier", + "documentation":"

    The rule ID.

    " + }, + "ruleVersion":{ + "shape":"nonEmptyString", + "documentation":"

    The rule version.

    " + } + }, + "documentation":"

    A rule.

    " + }, + "RuleDetail":{ + "type":"structure", + "members":{ + "ruleId":{ + "shape":"identifier", + "documentation":"

    The rule ID.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The rule description.

    " + }, + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector for which the rule is associated.

    " + }, + "ruleVersion":{ + "shape":"nonEmptyString", + "documentation":"

    The rule version.

    " + }, + "expression":{ + "shape":"ruleExpression", + "documentation":"

    The rule expression.

    " + }, + "language":{ + "shape":"Language", + "documentation":"

    The rule language.

    " + }, + "outcomes":{ + "shape":"NonEmptyListOfStrings", + "documentation":"

    The rule outcomes.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    Timestamp of the last time the rule was updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    The timestamp of when the rule was created.

    " + } + }, + "documentation":"

    The details of the rule.

    " + }, + "RuleDetailList":{ + "type":"list", + "member":{"shape":"RuleDetail"} + }, + "RuleList":{ + "type":"list", + "member":{"shape":"Rule"} + }, + "RulesMaxResults":{ + "type":"integer", + "box":true, + "max":100, + "min":50 + }, + "ThrottlingException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"string"} + }, + "documentation":"

    An exception indicating a throttling error.

    ", + "exception":true + }, + "TrainingDataSource":{ + "type":"structure", + "required":[ + "dataLocation", + "dataAccessRoleArn" + ], + "members":{ + "dataLocation":{ + "shape":"s3BucketLocation", + "documentation":"

    The data location of the training data source.

    " + }, + "dataAccessRoleArn":{ + "shape":"iamRoleArn", + "documentation":"

    The data access role ARN for the training data source.

    " + } + }, + "documentation":"

    The training data source.

    " + }, + "UpdateDetectorVersionMetadataRequest":{ + "type":"structure", + "required":[ + "detectorId", + "detectorVersionId", + "description" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The detector version ID.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The description.

    " + } + } + }, + "UpdateDetectorVersionMetadataResult":{ + "type":"structure", + "members":{ + } + }, + "UpdateDetectorVersionRequest":{ + "type":"structure", + "required":[ + "detectorId", + "detectorVersionId", + "externalModelEndpoints", + "rules" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The parent detector ID for the detector version you want to update.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The detector version ID.

    " + }, + "externalModelEndpoints":{ + "shape":"ListOfStrings", + "documentation":"

    The Amazon SageMaker model endpoints to include in the detector version.

    " + }, + "rules":{ + "shape":"RuleList", + "documentation":"

    The rules to include in the detector version.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The detector version description.

    " + }, + "modelVersions":{ + "shape":"ListOfModelVersions", + "documentation":"

    The model versions to include in the detector version.

    " + } + } + }, + "UpdateDetectorVersionResult":{ + "type":"structure", + "members":{ + } + }, + "UpdateDetectorVersionStatusRequest":{ + "type":"structure", + "required":[ + "detectorId", + "detectorVersionId", + "status" + ], + "members":{ + "detectorId":{ + "shape":"identifier", + "documentation":"

    The detector ID.

    " + }, + "detectorVersionId":{ + "shape":"nonEmptyString", + "documentation":"

    The detector version ID.

    " + }, + "status":{ + "shape":"DetectorVersionStatus", + "documentation":"

    The new status.

    " + } + } + }, + "UpdateDetectorVersionStatusResult":{ + "type":"structure", + "members":{ + } + }, + "UpdateModelVersionRequest":{ + "type":"structure", + "required":[ + "modelId", + "modelType", + "modelVersionNumber", + "description", + "status" + ], + "members":{ + "modelId":{ + "shape":"identifier", + "documentation":"

    The model ID.

    " + }, + "modelType":{ + "shape":"ModelTypeEnum", + "documentation":"

    The model type.

    " + }, + "modelVersionNumber":{ + "shape":"nonEmptyString", + "documentation":"

    The model version.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The model description.

    " + }, + "status":{ + "shape":"ModelVersionStatus", + "documentation":"

    The new model status.

    " + } + } + }, + "UpdateModelVersionResult":{ + "type":"structure", + "members":{ + } + }, + "UpdateRuleMetadataRequest":{ + "type":"structure", + "required":[ + "rule", + "description" + ], + "members":{ + "rule":{ + "shape":"Rule", + "documentation":"

    The rule to update.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The rule description.

    " + } + } + }, + "UpdateRuleMetadataResult":{ + "type":"structure", + "members":{ + } + }, + "UpdateRuleVersionRequest":{ + "type":"structure", + "required":[ + "rule", + "expression", + "language", + "outcomes" + ], + "members":{ + "rule":{ + "shape":"Rule", + "documentation":"

    The rule to update.

    " + }, + "description":{ + "shape":"description", + "documentation":"

    The description.

    " + }, + "expression":{ + "shape":"ruleExpression", + "documentation":"

    The rule expression.

    " + }, + "language":{ + "shape":"Language", + "documentation":"

    The language.

    " + }, + "outcomes":{ + "shape":"NonEmptyListOfStrings", + "documentation":"

    The outcomes.

    " + } + } + }, + "UpdateRuleVersionResult":{ + "type":"structure", + "members":{ + "rule":{ + "shape":"Rule", + "documentation":"

    The new rule version that was created.

    " + } + } + }, + "UpdateVariableRequest":{ + "type":"structure", + "required":["name"], + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The name of the variable.

    " + }, + "defaultValue":{ + "shape":"string", + "documentation":"

    The new default value of the variable.

    " + }, + "description":{ + "shape":"string", + "documentation":"

    The new description.

    " + }, + "variableType":{ + "shape":"string", + "documentation":"

    The variable type.

    " + } + } + }, + "UpdateVariableResult":{ + "type":"structure", + "members":{ + } + }, + "ValidationException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"string"} + }, + "documentation":"

    An exception indicating a specified value is not allowed.

    ", + "exception":true + }, + "Variable":{ + "type":"structure", + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The name of the variable.

    " + }, + "dataType":{ + "shape":"DataType", + "documentation":"

    The data type of the variable.

    " + }, + "dataSource":{ + "shape":"DataSource", + "documentation":"

    The data source of the variable.

    " + }, + "defaultValue":{ + "shape":"string", + "documentation":"

    The default value of the variable.

    " + }, + "description":{ + "shape":"string", + "documentation":"

    The description of the variable.

    " + }, + "variableType":{ + "shape":"string", + "documentation":"

    The variable type of the variable.

    " + }, + "lastUpdatedTime":{ + "shape":"time", + "documentation":"

    The time when variable was last updated.

    " + }, + "createdTime":{ + "shape":"time", + "documentation":"

    The time when the variable was created.

    " + } + }, + "documentation":"

    The variable.

    " + }, + "VariableEntry":{ + "type":"structure", + "members":{ + "name":{ + "shape":"string", + "documentation":"

    The name of the variable entry.

    " + }, + "dataType":{ + "shape":"string", + "documentation":"

    The data type of the variable entry.

    " + }, + "dataSource":{ + "shape":"string", + "documentation":"

    The data source of the variable entry.

    " + }, + "defaultValue":{ + "shape":"string", + "documentation":"

    The default value of the variable entry.

    " + }, + "description":{ + "shape":"string", + "documentation":"

    The description of the variable entry.

    " + }, + "variableType":{ + "shape":"string", + "documentation":"

    The type of the variable entry.

    " + } + }, + "documentation":"

    The variable entry in a list.

    " + }, + "VariableEntryList":{ + "type":"list", + "member":{"shape":"VariableEntry"}, + "max":25, + "min":1 + }, + "VariableList":{ + "type":"list", + "member":{"shape":"Variable"} + }, + "VariablesMaxResults":{ + "type":"integer", + "box":true, + "max":100, + "min":50 + }, + "attributeKey":{ + "type":"string", + "max":64, + "min":1 + }, + "attributeValue":{ + "type":"string", + "max":256, + "min":1, + "sensitive":true + }, + "blob":{"type":"blob"}, + "contentType":{ + "type":"string", + "max":1024, + "min":1 + }, + "description":{ + "type":"string", + "max":128, + "min":1 + }, + "float":{"type":"float"}, + "iamRoleArn":{ + "type":"string", + "max":256, + "min":1, + "pattern":"^arn\\:aws\\:iam\\:\\:[0-9]{12}\\:role\\/[^\\s]{2,64}$" + }, + "identifier":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^[0-9a-z_-]+$" + }, + "integer":{"type":"integer"}, + "nonEmptyString":{ + "type":"string", + "min":1 + }, + "ruleExpression":{ + "type":"string", + "max":4096, + "min":1 + }, + "s3BucketLocation":{ + "type":"string", + "max":512, + "min":1, + "pattern":"^s3:\\/\\/[^\\s]+$" + }, + "string":{"type":"string"}, + "time":{"type":"string"} + }, + "documentation":"

    This is the Amazon Fraud Detector API Reference. This guide is for developers who need detailed information about Amazon Fraud Detector API actions, data types, and errors. For more information about Amazon Fraud Detector features, see the Amazon Fraud Detector User Guide.

    " +} diff --git a/services/fsx/pom.xml b/services/fsx/pom.xml index 19fdac722eda..35d5c5767b33 100644 --- a/services/fsx/pom.xml +++ b/services/fsx/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + imagebuilder + AWS Java SDK :: Services :: Imagebuilder + The AWS Java SDK for Imagebuilder module holds the client classes that are used for + communicating with Imagebuilder. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.imagebuilder + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/imagebuilder/src/main/resources/codegen-resources/paginators-1.json b/services/imagebuilder/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..f1aa23df30c9 --- /dev/null +++ b/services/imagebuilder/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,49 @@ +{ + "pagination": { + "ListComponentBuildVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListComponents": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListDistributionConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListImageBuildVersions": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListImagePipelineImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListImagePipelines": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListImageRecipes": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListImages": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + }, + "ListInfrastructureConfigurations": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/services/imagebuilder/src/main/resources/codegen-resources/service-2.json b/services/imagebuilder/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..ea6a897302f2 --- /dev/null +++ b/services/imagebuilder/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,3666 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-12-02", + "endpointPrefix":"imagebuilder", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"imagebuilder", + "serviceFullName":"EC2 Image Builder", + "serviceId":"imagebuilder", + "signatureVersion":"v4", + "signingName":"imagebuilder", + "uid":"imagebuilder-2019-12-02" + }, + "operations":{ + "CancelImageCreation":{ + "name":"CancelImageCreation", + "http":{ + "method":"PUT", + "requestUri":"/CancelImageCreation" + }, + "input":{"shape":"CancelImageCreationRequest"}, + "output":{"shape":"CancelImageCreationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"} + ], + "documentation":"

    CancelImageCreation cancels the creation of Image. This operation can only be used on images in a non-terminal state.

    " + }, + "CreateComponent":{ + "name":"CreateComponent", + "http":{ + "method":"PUT", + "requestUri":"/CreateComponent" + }, + "input":{"shape":"CreateComponentRequest"}, + "output":{"shape":"CreateComponentResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"InvalidVersionNumberException"}, + {"shape":"ResourceInUseException"}, + {"shape":"InvalidParameterCombinationException"} + ], + "documentation":"

    Creates a new component that can be used to build, validate, test, and assess your image.

    " + }, + "CreateDistributionConfiguration":{ + "name":"CreateDistributionConfiguration", + "http":{ + "method":"PUT", + "requestUri":"/CreateDistributionConfiguration" + }, + "input":{"shape":"CreateDistributionConfigurationRequest"}, + "output":{"shape":"CreateDistributionConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ResourceAlreadyExistsException"}, + {"shape":"InvalidParameterCombinationException"} + ], + "documentation":"

    Creates a new distribution configuration. Distribution configurations define and configure the outputs of your pipeline.

    " + }, + "CreateImage":{ + "name":"CreateImage", + "http":{ + "method":"PUT", + "requestUri":"/CreateImage" + }, + "input":{"shape":"CreateImageRequest"}, + "output":{"shape":"CreateImageResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"} + ], + "documentation":"

    Creates a new image. This request will create a new image along with all of the configured output resources defined in the distribution configuration.

    " + }, + "CreateImagePipeline":{ + "name":"CreateImagePipeline", + "http":{ + "method":"PUT", + "requestUri":"/CreateImagePipeline" + }, + "input":{"shape":"CreateImagePipelineRequest"}, + "output":{"shape":"CreateImagePipelineResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ResourceAlreadyExistsException"} + ], + "documentation":"

    Creates a new image pipeline. Image pipelines enable you to automate the creation and distribution of images.

    " + }, + "CreateImageRecipe":{ + "name":"CreateImageRecipe", + "http":{ + "method":"PUT", + "requestUri":"/CreateImageRecipe" + }, + "input":{"shape":"CreateImageRecipeRequest"}, + "output":{"shape":"CreateImageRecipeResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"InvalidVersionNumberException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ResourceAlreadyExistsException"} + ], + "documentation":"

    Creates a new image recipe. Image recipes define how images are configured, tested, and assessed.

    " + }, + "CreateInfrastructureConfiguration":{ + "name":"CreateInfrastructureConfiguration", + "http":{ + "method":"PUT", + "requestUri":"/CreateInfrastructureConfiguration" + }, + "input":{"shape":"CreateInfrastructureConfigurationRequest"}, + "output":{"shape":"CreateInfrastructureConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ResourceAlreadyExistsException"} + ], + "documentation":"

    Creates a new infrastructure configuration. An infrastructure configuration defines the environment in which your image will be built and tested.

    " + }, + "DeleteComponent":{ + "name":"DeleteComponent", + "http":{ + "method":"DELETE", + "requestUri":"/DeleteComponent" + }, + "input":{"shape":"DeleteComponentRequest"}, + "output":{"shape":"DeleteComponentResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceDependencyException"} + ], + "documentation":"

    Deletes a component build version.

    " + }, + "DeleteDistributionConfiguration":{ + "name":"DeleteDistributionConfiguration", + "http":{ + "method":"DELETE", + "requestUri":"/DeleteDistributionConfiguration" + }, + "input":{"shape":"DeleteDistributionConfigurationRequest"}, + "output":{"shape":"DeleteDistributionConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceDependencyException"} + ], + "documentation":"

    Deletes a distribution configuration.

    " + }, + "DeleteImage":{ + "name":"DeleteImage", + "http":{ + "method":"DELETE", + "requestUri":"/DeleteImage" + }, + "input":{"shape":"DeleteImageRequest"}, + "output":{"shape":"DeleteImageResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceDependencyException"} + ], + "documentation":"

    Deletes an image.

    " + }, + "DeleteImagePipeline":{ + "name":"DeleteImagePipeline", + "http":{ + "method":"DELETE", + "requestUri":"/DeleteImagePipeline" + }, + "input":{"shape":"DeleteImagePipelineRequest"}, + "output":{"shape":"DeleteImagePipelineResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceDependencyException"} + ], + "documentation":"

    Deletes an image pipeline.

    " + }, + "DeleteImageRecipe":{ + "name":"DeleteImageRecipe", + "http":{ + "method":"DELETE", + "requestUri":"/DeleteImageRecipe" + }, + "input":{"shape":"DeleteImageRecipeRequest"}, + "output":{"shape":"DeleteImageRecipeResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceDependencyException"} + ], + "documentation":"

    Deletes an image recipe.

    " + }, + "DeleteInfrastructureConfiguration":{ + "name":"DeleteInfrastructureConfiguration", + "http":{ + "method":"DELETE", + "requestUri":"/DeleteInfrastructureConfiguration" + }, + "input":{"shape":"DeleteInfrastructureConfigurationRequest"}, + "output":{"shape":"DeleteInfrastructureConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceDependencyException"} + ], + "documentation":"

    Deletes an infrastructure configuration.

    " + }, + "GetComponent":{ + "name":"GetComponent", + "http":{ + "method":"GET", + "requestUri":"/GetComponent" + }, + "input":{"shape":"GetComponentRequest"}, + "output":{"shape":"GetComponentResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets a component object.

    " + }, + "GetComponentPolicy":{ + "name":"GetComponentPolicy", + "http":{ + "method":"GET", + "requestUri":"/GetComponentPolicy" + }, + "input":{"shape":"GetComponentPolicyRequest"}, + "output":{"shape":"GetComponentPolicyResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets a component policy.

    " + }, + "GetDistributionConfiguration":{ + "name":"GetDistributionConfiguration", + "http":{ + "method":"GET", + "requestUri":"/GetDistributionConfiguration" + }, + "input":{"shape":"GetDistributionConfigurationRequest"}, + "output":{"shape":"GetDistributionConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets a distribution configuration.

    " + }, + "GetImage":{ + "name":"GetImage", + "http":{ + "method":"GET", + "requestUri":"/GetImage" + }, + "input":{"shape":"GetImageRequest"}, + "output":{"shape":"GetImageResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets an image.

    " + }, + "GetImagePipeline":{ + "name":"GetImagePipeline", + "http":{ + "method":"GET", + "requestUri":"/GetImagePipeline" + }, + "input":{"shape":"GetImagePipelineRequest"}, + "output":{"shape":"GetImagePipelineResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets an image pipeline.

    " + }, + "GetImagePolicy":{ + "name":"GetImagePolicy", + "http":{ + "method":"GET", + "requestUri":"/GetImagePolicy" + }, + "input":{"shape":"GetImagePolicyRequest"}, + "output":{"shape":"GetImagePolicyResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets an image policy.

    " + }, + "GetImageRecipe":{ + "name":"GetImageRecipe", + "http":{ + "method":"GET", + "requestUri":"/GetImageRecipe" + }, + "input":{"shape":"GetImageRecipeRequest"}, + "output":{"shape":"GetImageRecipeResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets an image recipe.

    " + }, + "GetImageRecipePolicy":{ + "name":"GetImageRecipePolicy", + "http":{ + "method":"GET", + "requestUri":"/GetImageRecipePolicy" + }, + "input":{"shape":"GetImageRecipePolicyRequest"}, + "output":{"shape":"GetImageRecipePolicyResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets an image recipe policy.

    " + }, + "GetInfrastructureConfiguration":{ + "name":"GetInfrastructureConfiguration", + "http":{ + "method":"GET", + "requestUri":"/GetInfrastructureConfiguration" + }, + "input":{"shape":"GetInfrastructureConfigurationRequest"}, + "output":{"shape":"GetInfrastructureConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Gets an infrastructure configuration.

    " + }, + "ImportComponent":{ + "name":"ImportComponent", + "http":{ + "method":"PUT", + "requestUri":"/ImportComponent" + }, + "input":{"shape":"ImportComponentRequest"}, + "output":{"shape":"ImportComponentResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"InvalidVersionNumberException"}, + {"shape":"ResourceInUseException"}, + {"shape":"InvalidParameterCombinationException"} + ], + "documentation":"

    Imports a component and transforms its data into a component document.

    " + }, + "ListComponentBuildVersions":{ + "name":"ListComponentBuildVersions", + "http":{ + "method":"POST", + "requestUri":"/ListComponentBuildVersions" + }, + "input":{"shape":"ListComponentBuildVersionsRequest"}, + "output":{"shape":"ListComponentBuildVersionsResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns the list of component build versions for the specified semantic version.

    " + }, + "ListComponents":{ + "name":"ListComponents", + "http":{ + "method":"POST", + "requestUri":"/ListComponents" + }, + "input":{"shape":"ListComponentsRequest"}, + "output":{"shape":"ListComponentsResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns the list of component build versions for the specified semantic version.

    " + }, + "ListDistributionConfigurations":{ + "name":"ListDistributionConfigurations", + "http":{ + "method":"POST", + "requestUri":"/ListDistributionConfigurations" + }, + "input":{"shape":"ListDistributionConfigurationsRequest"}, + "output":{"shape":"ListDistributionConfigurationsResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns a list of distribution configurations.

    " + }, + "ListImageBuildVersions":{ + "name":"ListImageBuildVersions", + "http":{ + "method":"POST", + "requestUri":"/ListImageBuildVersions" + }, + "input":{"shape":"ListImageBuildVersionsRequest"}, + "output":{"shape":"ListImageBuildVersionsResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns a list of distribution configurations.

    " + }, + "ListImagePipelineImages":{ + "name":"ListImagePipelineImages", + "http":{ + "method":"POST", + "requestUri":"/ListImagePipelineImages" + }, + "input":{"shape":"ListImagePipelineImagesRequest"}, + "output":{"shape":"ListImagePipelineImagesResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns a list of images created by the specified pipeline.

    " + }, + "ListImagePipelines":{ + "name":"ListImagePipelines", + "http":{ + "method":"POST", + "requestUri":"/ListImagePipelines" + }, + "input":{"shape":"ListImagePipelinesRequest"}, + "output":{"shape":"ListImagePipelinesResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns a list of image pipelines.

    " + }, + "ListImageRecipes":{ + "name":"ListImageRecipes", + "http":{ + "method":"POST", + "requestUri":"/ListImageRecipes" + }, + "input":{"shape":"ListImageRecipesRequest"}, + "output":{"shape":"ListImageRecipesResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns a list of image recipes.

    " + }, + "ListImages":{ + "name":"ListImages", + "http":{ + "method":"POST", + "requestUri":"/ListImages" + }, + "input":{"shape":"ListImagesRequest"}, + "output":{"shape":"ListImagesResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns the list of image build versions for the specified semantic version.

    " + }, + "ListInfrastructureConfigurations":{ + "name":"ListInfrastructureConfigurations", + "http":{ + "method":"POST", + "requestUri":"/ListInfrastructureConfigurations" + }, + "input":{"shape":"ListInfrastructureConfigurationsRequest"}, + "output":{"shape":"ListInfrastructureConfigurationsResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidPaginationTokenException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Returns a list of infrastructure configurations.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"GET", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"InvalidParameterException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Returns the list of tags for the specified resource.

    " + }, + "PutComponentPolicy":{ + "name":"PutComponentPolicy", + "http":{ + "method":"PUT", + "requestUri":"/PutComponentPolicy" + }, + "input":{"shape":"PutComponentPolicyRequest"}, + "output":{"shape":"PutComponentPolicyResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Applies a policy to a component.

    " + }, + "PutImagePolicy":{ + "name":"PutImagePolicy", + "http":{ + "method":"PUT", + "requestUri":"/PutImagePolicy" + }, + "input":{"shape":"PutImagePolicyRequest"}, + "output":{"shape":"PutImagePolicyResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Applies a policy to an image.

    " + }, + "PutImageRecipePolicy":{ + "name":"PutImageRecipePolicy", + "http":{ + "method":"PUT", + "requestUri":"/PutImageRecipePolicy" + }, + "input":{"shape":"PutImageRecipePolicyRequest"}, + "output":{"shape":"PutImageRecipePolicyResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"InvalidParameterValueException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"} + ], + "documentation":"

    Applies a policy to an image recipe.

    " + }, + "StartImagePipelineExecution":{ + "name":"StartImagePipelineExecution", + "http":{ + "method":"PUT", + "requestUri":"/StartImagePipelineExecution" + }, + "input":{"shape":"StartImagePipelineExecutionRequest"}, + "output":{"shape":"StartImagePipelineExecutionResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"} + ], + "documentation":"

    Manually triggers a pipeline to create an image.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"InvalidParameterException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Adds a tag to a resource.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"DELETE", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"InvalidParameterException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Removes a tag from a resource.

    " + }, + "UpdateDistributionConfiguration":{ + "name":"UpdateDistributionConfiguration", + "http":{ + "method":"PUT", + "requestUri":"/UpdateDistributionConfiguration" + }, + "input":{"shape":"UpdateDistributionConfigurationRequest"}, + "output":{"shape":"UpdateDistributionConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"}, + {"shape":"InvalidParameterCombinationException"} + ], + "documentation":"

    Updates a new distribution configuration. Distribution configurations define and configure the outputs of your pipeline.

    " + }, + "UpdateImagePipeline":{ + "name":"UpdateImagePipeline", + "http":{ + "method":"PUT", + "requestUri":"/UpdateImagePipeline" + }, + "input":{"shape":"UpdateImagePipelineRequest"}, + "output":{"shape":"UpdateImagePipelineResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"} + ], + "documentation":"

    Updates a new image pipeline. Image pipelines enable you to automate the creation and distribution of images.

    " + }, + "UpdateInfrastructureConfiguration":{ + "name":"UpdateInfrastructureConfiguration", + "http":{ + "method":"PUT", + "requestUri":"/UpdateInfrastructureConfiguration" + }, + "input":{"shape":"UpdateInfrastructureConfigurationRequest"}, + "output":{"shape":"UpdateInfrastructureConfigurationResponse"}, + "errors":[ + {"shape":"ServiceException"}, + {"shape":"ClientException"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"InvalidRequestException"}, + {"shape":"IdempotentParameterMismatchException"}, + {"shape":"ForbiddenException"}, + {"shape":"CallRateLimitExceededException"}, + {"shape":"ResourceInUseException"} + ], + "documentation":"

    Updates a new infrastructure configuration. An infrastructure configuration defines the environment in which your image will be built and tested.

    " + } + }, + "shapes":{ + "AccountList":{ + "type":"list", + "member":{"shape":"NonEmptyString"} + }, + "Ami":{ + "type":"structure", + "members":{ + "region":{ + "shape":"NonEmptyString", + "documentation":"

    The AWS Region of the EC2 AMI.

    " + }, + "image":{ + "shape":"NonEmptyString", + "documentation":"

    The AMI ID of the EC2 AMI.

    " + }, + "name":{ + "shape":"NonEmptyString", + "documentation":"

    The name of the EC2 AMI.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the EC2 AMI.

    " + }, + "state":{"shape":"ImageState"} + }, + "documentation":"

    Details of an EC2 AMI.

    " + }, + "AmiDistributionConfiguration":{ + "type":"structure", + "members":{ + "name":{ + "shape":"AmiNameString", + "documentation":"

    The name of the distribution configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the distribution configuration.

    " + }, + "amiTags":{ + "shape":"TagMap", + "documentation":"

    The tags to apply to AMIs distributed to this Region.

    " + }, + "launchPermission":{ + "shape":"LaunchPermissionConfiguration", + "documentation":"

    Launch permissions can be used to configure which AWS accounts can use the AMI to launch instances.

    " + } + }, + "documentation":"

    Define and configure the output AMIs of the pipeline.

    " + }, + "AmiList":{ + "type":"list", + "member":{"shape":"Ami"} + }, + "AmiNameString":{ + "type":"string", + "max":127, + "min":1, + "pattern":"^[-_A-Za-z0-9{][-_A-Za-z0-9\\s:{}]+[-_A-Za-z0-9}]$" + }, + "Arn":{"type":"string"}, + "ArnList":{ + "type":"list", + "member":{"shape":"Arn"} + }, + "CallRateLimitExceededException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have exceeded the permitted request rate for the specific operation.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "CancelImageCreationRequest":{ + "type":"structure", + "required":[ + "imageBuildVersionArn", + "clientToken" + ], + "members":{ + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image whose creation you want to cancel.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "CancelImageCreationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image whose creation has been cancelled.

    " + } + } + }, + "ClientException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    These errors are usually caused by a client action, such as using an action or resource on behalf of a user that doesn't have permissions to use the action or resource, or specifying an invalid resource identifier.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ClientToken":{ + "type":"string", + "max":36, + "min":1 + }, + "Component":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the component.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The version of the component.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the component.

    " + }, + "changeDescription":{ + "shape":"NonEmptyString", + "documentation":"

    The change description of the component.

    " + }, + "type":{ + "shape":"ComponentType", + "documentation":"

    The type of the component denotes whether the component is used to build the image or only to test it.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the component.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the component.

    " + }, + "data":{ + "shape":"ComponentData", + "documentation":"

    The data of the component.

    " + }, + "kmsKeyId":{ + "shape":"NonEmptyString", + "documentation":"

    The KMS key identifier used to encrypt the component.

    " + }, + "encrypted":{ + "shape":"NullableBoolean", + "documentation":"

    The encryption status of the component.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date that the component was created.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags associated with the component.

    " + } + }, + "documentation":"

    A detailed view of a component.

    " + }, + "ComponentBuildVersionArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):component/[a-z0-9-_]+/\\d+\\.\\d+\\.\\d+/\\d+$" + }, + "ComponentConfiguration":{ + "type":"structure", + "required":["componentArn"], + "members":{ + "componentArn":{ + "shape":"ComponentVersionArnOrBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component.

    " + } + }, + "documentation":"

    Configuration details of the component.

    " + }, + "ComponentConfigurationList":{ + "type":"list", + "member":{"shape":"ComponentConfiguration"}, + "min":1 + }, + "ComponentData":{"type":"string"}, + "ComponentFormat":{ + "type":"string", + "enum":["SHELL"] + }, + "ComponentSummary":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the component.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The version of the component.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the component.

    " + }, + "type":{ + "shape":"ComponentType", + "documentation":"

    The type of the component denotes whether the component is used to build the image or only to test it.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the component.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the component.

    " + }, + "changeDescription":{ + "shape":"NonEmptyString", + "documentation":"

    The change description of the component.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date that the component was created.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags associated with the component.

    " + } + }, + "documentation":"

    A high-level summary of a component.

    " + }, + "ComponentSummaryList":{ + "type":"list", + "member":{"shape":"ComponentSummary"} + }, + "ComponentType":{ + "type":"string", + "enum":[ + "BUILD", + "TEST" + ] + }, + "ComponentVersion":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the component.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The semantic version of the component.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the component.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the component.

    " + }, + "type":{ + "shape":"ComponentType", + "documentation":"

    The type of the component denotes whether the component is used to build the image or only to test it.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the component.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date that the component was created.

    " + } + }, + "documentation":"

    A high-level overview of a component semantic version.

    " + }, + "ComponentVersionArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):component/[a-z0-9-_]+/\\d+\\.\\d+\\.\\d+$" + }, + "ComponentVersionArnOrBuildVersionArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):component/[a-z0-9-_]+/(?:(?:(\\d+|x)\\.(\\d+|x)\\.(\\d+|x))|(?:\\d+\\.\\d+\\.\\d+/\\d+))$" + }, + "ComponentVersionList":{ + "type":"list", + "member":{"shape":"ComponentVersion"} + }, + "CreateComponentRequest":{ + "type":"structure", + "required":[ + "name", + "semanticVersion", + "platform", + "clientToken" + ], + "members":{ + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the component.

    " + }, + "semanticVersion":{ + "shape":"VersionNumber", + "documentation":"

    The semantic version of the component. This version follows the semantic version syntax. For example, major.minor.patch. This could be versioned like software (2.0.1) or like a date (2019.12.01).

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the component. Describes the contents of the component.

    " + }, + "changeDescription":{ + "shape":"NonEmptyString", + "documentation":"

    The change description of the component. Describes what change has been made in this version, or what makes this version different from other versions of this component.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the component.

    " + }, + "data":{ + "shape":"InlineComponentData", + "documentation":"

    The data of the component. Used to specify the data inline. Either data or uri can be used to specify the data within the component.

    " + }, + "uri":{ + "shape":"Uri", + "documentation":"

    The uri of the component. Must be an S3 URL and the requester must have permission to access the S3 bucket. If you use S3, you can specify component content up to your service quota. Either data or uri can be used to specify the data within the component.

    " + }, + "kmsKeyId":{ + "shape":"NonEmptyString", + "documentation":"

    The ID of the KMS key that should be used to encrypt this component.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the component.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token of the component.

    ", + "idempotencyToken":true + } + } + }, + "CreateComponentResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "componentBuildVersionArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component that was created by this request.

    " + } + } + }, + "CreateDistributionConfigurationRequest":{ + "type":"structure", + "required":[ + "name", + "distributions", + "clientToken" + ], + "members":{ + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the distribution configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the distribution configuration.

    " + }, + "distributions":{ + "shape":"DistributionList", + "documentation":"

    The distributions of the distribution configuration.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the distribution configuration.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token of the distribution configuration.

    ", + "idempotencyToken":true + } + } + }, + "CreateDistributionConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that was created by this request.

    " + } + } + }, + "CreateImagePipelineRequest":{ + "type":"structure", + "required":[ + "name", + "imageRecipeArn", + "infrastructureConfigurationArn", + "clientToken" + ], + "members":{ + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image pipeline.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the image pipeline.

    " + }, + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that will be used to configure images created by this image pipeline.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that will be used to build images created by this image pipeline.

    " + }, + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that will be used to configure and distribute images created by this image pipeline.

    " + }, + "imageTestsConfiguration":{ + "shape":"ImageTestsConfiguration", + "documentation":"

    The image test configuration of the image pipeline.

    " + }, + "schedule":{ + "shape":"Schedule", + "documentation":"

    The schedule of the image pipeline.

    " + }, + "status":{ + "shape":"PipelineStatus", + "documentation":"

    The status of the image pipeline.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image pipeline.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "CreateImagePipelineResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that was created by this request.

    " + } + } + }, + "CreateImageRecipeRequest":{ + "type":"structure", + "required":[ + "name", + "semanticVersion", + "components", + "parentImage", + "clientToken" + ], + "members":{ + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image recipe.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the image recipe.

    " + }, + "semanticVersion":{ + "shape":"VersionNumber", + "documentation":"

    The semantic version of the image recipe.

    " + }, + "components":{ + "shape":"ComponentConfigurationList", + "documentation":"

    The components of the image recipe.

    " + }, + "parentImage":{ + "shape":"NonEmptyString", + "documentation":"

    The parent image of the image recipe.

    " + }, + "blockDeviceMappings":{ + "shape":"InstanceBlockDeviceMappings", + "documentation":"

    The block device mappings of the image recipe.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image recipe.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "CreateImageRecipeResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that was created by this request.

    " + } + } + }, + "CreateImageRequest":{ + "type":"structure", + "required":[ + "imageRecipeArn", + "infrastructureConfigurationArn", + "clientToken" + ], + "members":{ + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that defines how images are configured, tested, and assessed.

    " + }, + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that defines and configures the outputs of your pipeline.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that defines the environment in which your image will be built and tested.

    " + }, + "imageTestsConfiguration":{ + "shape":"ImageTestsConfiguration", + "documentation":"

    The image tests configuration of the image.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "CreateImageResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image that was created by this request.

    " + } + } + }, + "CreateInfrastructureConfigurationRequest":{ + "type":"structure", + "required":[ + "name", + "instanceProfileName", + "clientToken" + ], + "members":{ + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the infrastructure configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the infrastructure configuration.

    " + }, + "instanceTypes":{ + "shape":"InstanceTypeList", + "documentation":"

    The instance types of the infrastructure configuration. You can specify one or more instance types to use for this build. The service will pick one of these instance types based on availability.

    " + }, + "instanceProfileName":{ + "shape":"NonEmptyString", + "documentation":"

    The instance profile to associate with the instance used to customize your EC2 AMI.

    " + }, + "securityGroupIds":{ + "shape":"SecurityGroupIds", + "documentation":"

    The security group IDs to associate with the instance used to customize your EC2 AMI.

    " + }, + "subnetId":{ + "shape":"NonEmptyString", + "documentation":"

    The subnet ID in which to place the instance used to customize your EC2 AMI.

    " + }, + "logging":{ + "shape":"Logging", + "documentation":"

    The logging configuration of the infrastructure configuration.

    " + }, + "keyPair":{ + "shape":"NonEmptyString", + "documentation":"

    The key pair of the infrastructure configuration. This can be used to log on to and debug the instance used to create your image.

    " + }, + "terminateInstanceOnFailure":{ + "shape":"NullableBoolean", + "documentation":"

    The terminate instance on failure setting of the infrastructure configuration. Set to false if you want Image Builder to retain the instance used to configure your AMI if the build or test phase of your workflow fails.

    " + }, + "snsTopicArn":{ + "shape":"SnsTopicArn", + "documentation":"

    The SNS topic on which to send image build events.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the infrastructure configuration.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "CreateInfrastructureConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that was created by this request.

    " + } + } + }, + "DateTime":{"type":"string"}, + "DeleteComponentRequest":{ + "type":"structure", + "required":["componentBuildVersionArn"], + "members":{ + "componentBuildVersionArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component build version to delete.

    ", + "location":"querystring", + "locationName":"componentBuildVersionArn" + } + } + }, + "DeleteComponentResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "componentBuildVersionArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component build version that was deleted.

    " + } + } + }, + "DeleteDistributionConfigurationRequest":{ + "type":"structure", + "required":["distributionConfigurationArn"], + "members":{ + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration to delete.

    ", + "location":"querystring", + "locationName":"distributionConfigurationArn" + } + } + }, + "DeleteDistributionConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that was deleted.

    " + } + } + }, + "DeleteImagePipelineRequest":{ + "type":"structure", + "required":["imagePipelineArn"], + "members":{ + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline to delete.

    ", + "location":"querystring", + "locationName":"imagePipelineArn" + } + } + }, + "DeleteImagePipelineResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that was deleted.

    " + } + } + }, + "DeleteImageRecipeRequest":{ + "type":"structure", + "required":["imageRecipeArn"], + "members":{ + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe to delete.

    ", + "location":"querystring", + "locationName":"imageRecipeArn" + } + } + }, + "DeleteImageRecipeResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that was deleted.

    " + } + } + }, + "DeleteImageRequest":{ + "type":"structure", + "required":["imageBuildVersionArn"], + "members":{ + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image to delete.

    ", + "location":"querystring", + "locationName":"imageBuildVersionArn" + } + } + }, + "DeleteImageResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image that was deleted.

    " + } + } + }, + "DeleteInfrastructureConfigurationRequest":{ + "type":"structure", + "required":["infrastructureConfigurationArn"], + "members":{ + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration to delete.

    ", + "location":"querystring", + "locationName":"infrastructureConfigurationArn" + } + } + }, + "DeleteInfrastructureConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that was deleted.

    " + } + } + }, + "Distribution":{ + "type":"structure", + "required":["region"], + "members":{ + "region":{ + "shape":"NonEmptyString", + "documentation":"

    The target Region.

    " + }, + "amiDistributionConfiguration":{ + "shape":"AmiDistributionConfiguration", + "documentation":"

    The specific AMI settings (for example, launch permissions, AMI tags).

    " + }, + "licenseConfigurationArns":{ + "shape":"ArnList", + "documentation":"

    The License Manager Configuration to associate with the AMI in the specified Region.

    " + } + }, + "documentation":"

    Defines the settings for a specific Region.

    " + }, + "DistributionConfiguration":{ + "type":"structure", + "required":["timeoutMinutes"], + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the distribution configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the distribution configuration.

    " + }, + "distributions":{ + "shape":"DistributionList", + "documentation":"

    The distributions of the distribution configuration.

    " + }, + "timeoutMinutes":{ + "shape":"DistributionTimeoutMinutes", + "documentation":"

    The maximum duration in minutes for this distribution configuration.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which this distribution configuration was created.

    " + }, + "dateUpdated":{ + "shape":"DateTime", + "documentation":"

    The date on which this distribution configuration was last updated.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the distribution configuration.

    " + } + }, + "documentation":"

    A distribution configuration.

    " + }, + "DistributionConfigurationArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):distribution-configuration/[a-z0-9-_]+$" + }, + "DistributionConfigurationSummary":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the distribution configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the distribution configuration.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which the distribution configuration was created.

    " + }, + "dateUpdated":{ + "shape":"DateTime", + "documentation":"

    The date on which the distribution configuration was updated.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags associated with the distribution configuration.

    " + } + }, + "documentation":"

    A high-level overview of a distribution configuration.

    " + }, + "DistributionConfigurationSummaryList":{ + "type":"list", + "member":{"shape":"DistributionConfigurationSummary"} + }, + "DistributionList":{ + "type":"list", + "member":{"shape":"Distribution"} + }, + "DistributionTimeoutMinutes":{ + "type":"integer", + "max":720, + "min":30 + }, + "EbsInstanceBlockDeviceSpecification":{ + "type":"structure", + "members":{ + "encrypted":{ + "shape":"NullableBoolean", + "documentation":"

    Use to configure device encryption.

    " + }, + "deleteOnTermination":{ + "shape":"NullableBoolean", + "documentation":"

    Use to configure delete on termination of the associated device.

    " + }, + "iops":{ + "shape":"EbsIopsInteger", + "documentation":"

    Use to configure device IOPS.

    " + }, + "kmsKeyId":{ + "shape":"NonEmptyString", + "documentation":"

    Use to configure the KMS key to use when encrypting the device.

    " + }, + "snapshotId":{ + "shape":"NonEmptyString", + "documentation":"

    The snapshot that defines the device contents.

    " + }, + "volumeSize":{ + "shape":"EbsVolumeSizeInteger", + "documentation":"

    Use to override the device's volume size.

    " + }, + "volumeType":{ + "shape":"EbsVolumeType", + "documentation":"

    Use to override the device's volume type.

    " + } + }, + "documentation":"

    Amazon EBS-specific block device mapping specifications.

    " + }, + "EbsIopsInteger":{ + "type":"integer", + "max":10000, + "min":100 + }, + "EbsVolumeSizeInteger":{ + "type":"integer", + "max":16000, + "min":1 + }, + "EbsVolumeType":{ + "type":"string", + "enum":[ + "standard", + "io1", + "gp2", + "sc1", + "st1" + ] + }, + "EmptyString":{ + "type":"string", + "max":0, + "min":0 + }, + "ErrorMessage":{"type":"string"}, + "Filter":{ + "type":"structure", + "members":{ + "name":{ + "shape":"FilterName", + "documentation":"

    The name of the filter. Filter names are case-sensitive.

    " + }, + "values":{ + "shape":"FilterValues", + "documentation":"

    The filter values. Filter values are case-sensitive.

    " + } + }, + "documentation":"

    A filter name and value pair that is used to return a more specific list of results from a list operation. Filters can be used to match a set of resources by specific criteria, such as tags, attributes, or IDs.

    " + }, + "FilterList":{ + "type":"list", + "member":{"shape":"Filter"}, + "max":10, + "min":1 + }, + "FilterName":{ + "type":"string", + "pattern":"^[a-zA-Z]{1,1024}$" + }, + "FilterValue":{ + "type":"string", + "pattern":"^[0-9a-zA-Z./_ :-]{1,1024}$" + }, + "FilterValues":{ + "type":"list", + "member":{"shape":"FilterValue"}, + "max":10, + "min":1 + }, + "ForbiddenException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You are not authorized to perform the requested operation.

    ", + "error":{"httpStatusCode":403}, + "exception":true + }, + "GetComponentPolicyRequest":{ + "type":"structure", + "required":["componentArn"], + "members":{ + "componentArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component whose policy you want to retrieve.

    ", + "location":"querystring", + "locationName":"componentArn" + } + } + }, + "GetComponentPolicyResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "policy":{ + "shape":"ResourcePolicyDocument", + "documentation":"

    The component policy.

    " + } + } + }, + "GetComponentRequest":{ + "type":"structure", + "required":["componentBuildVersionArn"], + "members":{ + "componentBuildVersionArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component that you want to retrieve. Regex requires \"/\\d+$\" suffix.

    ", + "location":"querystring", + "locationName":"componentBuildVersionArn" + } + } + }, + "GetComponentResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "component":{ + "shape":"Component", + "documentation":"

    The component object associated with the specified ARN.

    " + } + } + }, + "GetDistributionConfigurationRequest":{ + "type":"structure", + "required":["distributionConfigurationArn"], + "members":{ + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that you want to retrieve.

    ", + "location":"querystring", + "locationName":"distributionConfigurationArn" + } + } + }, + "GetDistributionConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "distributionConfiguration":{ + "shape":"DistributionConfiguration", + "documentation":"

    The distribution configuration object.

    " + } + } + }, + "GetImagePipelineRequest":{ + "type":"structure", + "required":["imagePipelineArn"], + "members":{ + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that you want to retrieve.

    ", + "location":"querystring", + "locationName":"imagePipelineArn" + } + } + }, + "GetImagePipelineResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imagePipeline":{ + "shape":"ImagePipeline", + "documentation":"

    The image pipeline object.

    " + } + } + }, + "GetImagePolicyRequest":{ + "type":"structure", + "required":["imageArn"], + "members":{ + "imageArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image whose policy you want to retrieve.

    ", + "location":"querystring", + "locationName":"imageArn" + } + } + }, + "GetImagePolicyResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "policy":{ + "shape":"ResourcePolicyDocument", + "documentation":"

    The image policy object.

    " + } + } + }, + "GetImageRecipePolicyRequest":{ + "type":"structure", + "required":["imageRecipeArn"], + "members":{ + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe whose policy you want to retrieve.

    ", + "location":"querystring", + "locationName":"imageRecipeArn" + } + } + }, + "GetImageRecipePolicyResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "policy":{ + "shape":"ResourcePolicyDocument", + "documentation":"

    The image recipe policy object.

    " + } + } + }, + "GetImageRecipeRequest":{ + "type":"structure", + "required":["imageRecipeArn"], + "members":{ + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that you want to retrieve.

    ", + "location":"querystring", + "locationName":"imageRecipeArn" + } + } + }, + "GetImageRecipeResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageRecipe":{ + "shape":"ImageRecipe", + "documentation":"

    The image recipe object.

    " + } + } + }, + "GetImageRequest":{ + "type":"structure", + "required":["imageBuildVersionArn"], + "members":{ + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image that you want to retrieve.

    ", + "location":"querystring", + "locationName":"imageBuildVersionArn" + } + } + }, + "GetImageResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "image":{ + "shape":"Image", + "documentation":"

    The image object.

    " + } + } + }, + "GetInfrastructureConfigurationRequest":{ + "type":"structure", + "required":["infrastructureConfigurationArn"], + "members":{ + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that you want to retrieve.

    ", + "location":"querystring", + "locationName":"infrastructureConfigurationArn" + } + }, + "documentation":"

    GetInfrastructureConfiguration request object.

    " + }, + "GetInfrastructureConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "infrastructureConfiguration":{ + "shape":"InfrastructureConfiguration", + "documentation":"

    The infrastructure configuration object.

    " + } + }, + "documentation":"

    GetInfrastructureConfiguration response object.

    " + }, + "IdempotentParameterMismatchException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have specified a client token for an operation using parameter values that differ from a previous request that used the same client token.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "Image":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The semantic version of the image.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the image.

    " + }, + "state":{ + "shape":"ImageState", + "documentation":"

    The state of the image.

    " + }, + "imageRecipe":{ + "shape":"ImageRecipe", + "documentation":"

    The image recipe used when creating the image.

    " + }, + "sourcePipelineName":{ + "shape":"ResourceName", + "documentation":"

    The name of the image pipeline that created this image.

    " + }, + "sourcePipelineArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that created this image.

    " + }, + "infrastructureConfiguration":{ + "shape":"InfrastructureConfiguration", + "documentation":"

    The infrastructure used when creating this image.

    " + }, + "distributionConfiguration":{ + "shape":"DistributionConfiguration", + "documentation":"

    The distribution configuration used when creating this image.

    " + }, + "imageTestsConfiguration":{ + "shape":"ImageTestsConfiguration", + "documentation":"

    The image tests configuration used when creating this image.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which this image was created.

    " + }, + "outputResources":{ + "shape":"OutputResources", + "documentation":"

    The output resources produced when creating this image.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image.

    " + } + }, + "documentation":"

    An image build version.

    " + }, + "ImageBuildVersionArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):image/[a-z0-9-_]+/\\d+\\.\\d+\\.\\d+/\\d+$" + }, + "ImageBuilderArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):(?:image-recipe|infrastructure-configuration|distribution-configuration|component|image|image-pipeline)/[a-z0-9-_]+(?:/(?:(?:x|\\d+)\\.(?:x|\\d+)\\.(?:x|\\d+))(?:/\\d+)?)?$" + }, + "ImagePipeline":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image pipeline.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the image pipeline.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the image pipeline.

    " + }, + "imageRecipeArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe associated with this image pipeline.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration associated with this image pipeline.

    " + }, + "distributionConfigurationArn":{ + "shape":"Arn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration associated with this image pipeline.

    " + }, + "imageTestsConfiguration":{ + "shape":"ImageTestsConfiguration", + "documentation":"

    The image tests configuration of the image pipeline.

    " + }, + "schedule":{ + "shape":"Schedule", + "documentation":"

    The schedule of the image pipeline.

    " + }, + "status":{ + "shape":"PipelineStatus", + "documentation":"

    The status of the image pipeline.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which this image pipeline was created.

    " + }, + "dateUpdated":{ + "shape":"DateTime", + "documentation":"

    The date on which this image pipeline was last updated.

    " + }, + "dateLastRun":{ + "shape":"DateTime", + "documentation":"

    The date on which this image pipeline was last run.

    " + }, + "dateNextRun":{ + "shape":"DateTime", + "documentation":"

    The date on which this image pipeline will next be run.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of this image pipeline.

    " + } + }, + "documentation":"

    Details of an image pipeline.

    " + }, + "ImagePipelineArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):image-pipeline/[a-z0-9-_]+$" + }, + "ImagePipelineList":{ + "type":"list", + "member":{"shape":"ImagePipeline"} + }, + "ImageRecipe":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image recipe.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the image recipe.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the image recipe.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the image recipe.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The version of the image recipe.

    " + }, + "components":{ + "shape":"ComponentConfigurationList", + "documentation":"

    The components of the image recipe.

    " + }, + "parentImage":{ + "shape":"NonEmptyString", + "documentation":"

    The parent image of the image recipe.

    " + }, + "blockDeviceMappings":{ + "shape":"InstanceBlockDeviceMappings", + "documentation":"

    The block device mappings to apply when creating images from this recipe.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which this image recipe was created.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image recipe.

    " + } + }, + "documentation":"

    An image recipe.

    " + }, + "ImageRecipeArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):image-recipe/[a-z0-9-_]+/\\d+\\.\\d+\\.\\d+$" + }, + "ImageRecipeSummary":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image recipe.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the image recipe.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the image recipe.

    " + }, + "parentImage":{ + "shape":"NonEmptyString", + "documentation":"

    The parent image of the image recipe.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which this image recipe was created.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image recipe.

    " + } + }, + "documentation":"

    A summary of an image recipe.

    " + }, + "ImageRecipeSummaryList":{ + "type":"list", + "member":{"shape":"ImageRecipeSummary"} + }, + "ImageState":{ + "type":"structure", + "members":{ + "status":{ + "shape":"ImageStatus", + "documentation":"

    The status of the image.

    " + }, + "reason":{ + "shape":"NonEmptyString", + "documentation":"

    The reason for the image's status.

    " + } + }, + "documentation":"

    Image state shows the image status and the reason for that status.

    " + }, + "ImageStatus":{ + "type":"string", + "enum":[ + "PENDING", + "CREATING", + "BUILDING", + "TESTING", + "DISTRIBUTING", + "INTEGRATING", + "AVAILABLE", + "CANCELLED", + "FAILED", + "DEPRECATED", + "DELETED" + ] + }, + "ImageSummary":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The version of the image.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the image.

    " + }, + "state":{ + "shape":"ImageState", + "documentation":"

    The state of the image.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the image.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which this image was created.

    " + }, + "outputResources":{ + "shape":"OutputResources", + "documentation":"

    The output resources produced when creating this image.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the image.

    " + } + }, + "documentation":"

    An image summary.

    " + }, + "ImageSummaryList":{ + "type":"list", + "member":{"shape":"ImageSummary"} + }, + "ImageTestsConfiguration":{ + "type":"structure", + "members":{ + "imageTestsEnabled":{ + "shape":"NullableBoolean", + "documentation":"

    Defines if tests should be executed when building this image.

    " + }, + "timeoutMinutes":{ + "shape":"ImageTestsTimeoutMinutes", + "documentation":"

    The maximum time in minutes that tests are permitted to run.

    " + } + }, + "documentation":"

    Image tests configuration.

    " + }, + "ImageTestsTimeoutMinutes":{ + "type":"integer", + "max":1440, + "min":60 + }, + "ImageVersion":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image semantic version.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the image semantic version.

    " + }, + "version":{ + "shape":"VersionNumber", + "documentation":"

    The semantic version of the image semantic version.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the image semantic version.

    " + }, + "owner":{ + "shape":"NonEmptyString", + "documentation":"

    The owner of the image semantic version.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date at which this image semantic version was created.

    " + } + }, + "documentation":"

    An image semantic version.

    " + }, + "ImageVersionArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):image/[a-z0-9-_]+/\\d+\\.\\d+\\.\\d+$" + }, + "ImageVersionList":{ + "type":"list", + "member":{"shape":"ImageVersion"} + }, + "ImportComponentRequest":{ + "type":"structure", + "required":[ + "name", + "semanticVersion", + "type", + "format", + "platform", + "clientToken" + ], + "members":{ + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the component.

    " + }, + "semanticVersion":{ + "shape":"VersionNumber", + "documentation":"

    The semantic version of the component. This version follows the semantic version syntax. For example, major.minor.patch. This could be versioned like software (2.0.1) or like a date (2019.12.01).

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the component. Describes the contents of the component.

    " + }, + "changeDescription":{ + "shape":"NonEmptyString", + "documentation":"

    The change description of the component. Describes what change has been made in this version, or what makes this version different from other versions of this component.

    " + }, + "type":{ + "shape":"ComponentType", + "documentation":"

    The type of the component denotes whether the component is used to build the image or only to test it.

    " + }, + "format":{ + "shape":"ComponentFormat", + "documentation":"

    The format of the resource that you want to import as a component.

    " + }, + "platform":{ + "shape":"Platform", + "documentation":"

    The platform of the component.

    " + }, + "data":{ + "shape":"NonEmptyString", + "documentation":"

    The data of the component. Used to specify the data inline. Either data or uri can be used to specify the data within the component.

    " + }, + "uri":{ + "shape":"Uri", + "documentation":"

    The uri of the component. Must be an S3 URL and the requester must have permission to access the S3 bucket. If you use S3, you can specify component content up to your service quota. Either data or uri can be used to specify the data within the component.

    " + }, + "kmsKeyId":{ + "shape":"NonEmptyString", + "documentation":"

    The ID of the KMS key that should be used to encrypt this component.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the component.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token of the component.

    ", + "idempotencyToken":true + } + } + }, + "ImportComponentResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "componentBuildVersionArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the imported component.

    " + } + } + }, + "InfrastructureConfiguration":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the infrastructure configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the infrastructure configuration.

    " + }, + "instanceTypes":{ + "shape":"InstanceTypeList", + "documentation":"

    The instance types of the infrastructure configuration.

    " + }, + "instanceProfileName":{ + "shape":"NonEmptyString", + "documentation":"

    The instance profile of the infrastructure configuration.

    " + }, + "securityGroupIds":{ + "shape":"SecurityGroupIds", + "documentation":"

    The security group IDs of the infrastructure configuration.

    " + }, + "subnetId":{ + "shape":"NonEmptyString", + "documentation":"

    The subnet ID of the infrastructure configuration.

    " + }, + "logging":{ + "shape":"Logging", + "documentation":"

    The logging configuration of the infrastructure configuration.

    " + }, + "keyPair":{ + "shape":"NonEmptyString", + "documentation":"

    The EC2 key pair of the infrastructure configuration.

    " + }, + "terminateInstanceOnFailure":{ + "shape":"NullableBoolean", + "documentation":"

    The terminate instance on failure configuration of the infrastructure configuration.

    " + }, + "snsTopicArn":{ + "shape":"NonEmptyString", + "documentation":"

    The SNS topic Amazon Resource Name (ARN) of the infrastructure configuration.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which the infrastructure configuration was created.

    " + }, + "dateUpdated":{ + "shape":"DateTime", + "documentation":"

    The date on which the infrastructure configuration was last updated.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the infrastructure configuration.

    " + } + }, + "documentation":"

    Details of the infrastructure configuration.

    " + }, + "InfrastructureConfigurationArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:imagebuilder:[^:]+:(?:\\d{12}|aws):infrastructure-configuration/[a-z0-9-_]+$" + }, + "InfrastructureConfigurationSummary":{ + "type":"structure", + "members":{ + "arn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration.

    " + }, + "name":{ + "shape":"ResourceName", + "documentation":"

    The name of the infrastructure configuration.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the infrastructure configuration.

    " + }, + "dateCreated":{ + "shape":"DateTime", + "documentation":"

    The date on which the infrastructure configuration was created.

    " + }, + "dateUpdated":{ + "shape":"DateTime", + "documentation":"

    The date on which the infrastructure configuration was last updated.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags of the infrastructure configuration.

    " + } + }, + "documentation":"

    The infrastructure used when building EC2 AMIs.

    " + }, + "InfrastructureConfigurationSummaryList":{ + "type":"list", + "member":{"shape":"InfrastructureConfigurationSummary"} + }, + "InlineComponentData":{ + "type":"string", + "max":16000, + "min":1 + }, + "InstanceBlockDeviceMapping":{ + "type":"structure", + "members":{ + "deviceName":{ + "shape":"NonEmptyString", + "documentation":"

    The device to which these mappings apply.

    " + }, + "ebs":{ + "shape":"EbsInstanceBlockDeviceSpecification", + "documentation":"

    Use to manage Amazon EBS-specific configuration for this mapping.

    " + }, + "virtualName":{ + "shape":"NonEmptyString", + "documentation":"

    Use to manage instance ephemeral devices.

    " + }, + "noDevice":{ + "shape":"EmptyString", + "documentation":"

    Use to remove a mapping from the parent image.

    " + } + }, + "documentation":"

    Defines block device mappings for the instance used to configure your image.

    " + }, + "InstanceBlockDeviceMappings":{ + "type":"list", + "member":{"shape":"InstanceBlockDeviceMapping"} + }, + "InstanceType":{"type":"string"}, + "InstanceTypeList":{ + "type":"list", + "member":{"shape":"InstanceType"} + }, + "InvalidPaginationTokenException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have provided an invalid pagination token in your request.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidParameterCombinationException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have specified two or more mutually exclusive parameters. Review the error message for details.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidParameterException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The specified parameter is invalid. Review the available parameters for the API request.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidParameterValueException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The value that you provided for the specified parameter is invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidRequestException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have made a request for an action that is not supported by the service.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidVersionNumberException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Your version number is out of bounds or does not follow the required syntax.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "LaunchPermissionConfiguration":{ + "type":"structure", + "members":{ + "userIds":{ + "shape":"AccountList", + "documentation":"

    The AWS account ID.

    " + }, + "userGroups":{ + "shape":"StringList", + "documentation":"

    The name of the group.

    " + } + }, + "documentation":"

    Describes the configuration for a launch permission. The launch permission modification request is sent to the EC2 ModifyImageAttribute API on behalf of the user for each Region they have selected to distribute the AMI.

    " + }, + "ListComponentBuildVersionsRequest":{ + "type":"structure", + "required":["componentVersionArn"], + "members":{ + "componentVersionArn":{ + "shape":"ComponentVersionArn", + "documentation":"

    The component version Amazon Resource Name (ARN) whose versions you want to list.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListComponentBuildVersionsResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "componentSummaryList":{ + "shape":"ComponentSummaryList", + "documentation":"

    The list of component summaries for the specified semantic version.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListComponentsRequest":{ + "type":"structure", + "members":{ + "owner":{ + "shape":"Ownership", + "documentation":"

    The owner defines which components you want to list. By default, this request will only show components owned by your account. You can use this field to specify if you want to view components owned by yourself, by Amazon, or those components that have been shared with you by other customers.

    " + }, + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListComponentsResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "componentVersionList":{ + "shape":"ComponentVersionList", + "documentation":"

    The list of component semantic versions.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListDistributionConfigurationsRequest":{ + "type":"structure", + "members":{ + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListDistributionConfigurationsResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "distributionConfigurationSummaryList":{ + "shape":"DistributionConfigurationSummaryList", + "documentation":"

    The list of distributions.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListImageBuildVersionsRequest":{ + "type":"structure", + "required":["imageVersionArn"], + "members":{ + "imageVersionArn":{ + "shape":"ImageVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image whose build versions you want to retrieve.

    " + }, + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListImageBuildVersionsResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageSummaryList":{ + "shape":"ImageSummaryList", + "documentation":"

    The list of image build versions.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListImagePipelineImagesRequest":{ + "type":"structure", + "required":["imagePipelineArn"], + "members":{ + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline whose images you want to view.

    " + }, + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListImagePipelineImagesResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageSummaryList":{ + "shape":"ImageSummaryList", + "documentation":"

    The list of images built by this pipeline.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListImagePipelinesRequest":{ + "type":"structure", + "members":{ + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListImagePipelinesResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imagePipelineList":{ + "shape":"ImagePipelineList", + "documentation":"

    The list of image pipelines.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListImageRecipesRequest":{ + "type":"structure", + "members":{ + "owner":{ + "shape":"Ownership", + "documentation":"

    The owner defines which image recipes you want to list. By default, this request will only show image recipes owned by your account. You can use this field to specify if you want to view image recipes owned by yourself, by Amazon, or those image recipes that have been shared with you by other customers.

    " + }, + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListImageRecipesResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageRecipeSummaryList":{ + "shape":"ImageRecipeSummaryList", + "documentation":"

    The list of image pipelines.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListImagesRequest":{ + "type":"structure", + "members":{ + "owner":{ + "shape":"Ownership", + "documentation":"

    The owner defines which images you want to list. By default, this request will only show images owned by your account. You can use this field to specify if you want to view images owned by yourself, by Amazon, or those images that have been shared with you by other customers.

    " + }, + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListImagesResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageVersionList":{ + "shape":"ImageVersionList", + "documentation":"

    The list of image semantic versions.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListInfrastructureConfigurationsRequest":{ + "type":"structure", + "members":{ + "filters":{ + "shape":"FilterList", + "documentation":"

    The filters.

    " + }, + "maxResults":{ + "shape":"RestrictedInteger", + "documentation":"

    The maximum items to return in a request.

    ", + "box":true + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    A token to specify where to start paginating. This is the NextToken from a previously truncated response.

    " + } + } + }, + "ListInfrastructureConfigurationsResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "infrastructureConfigurationSummaryList":{ + "shape":"InfrastructureConfigurationSummaryList", + "documentation":"

    The list of infrastructure configurations.

    " + }, + "nextToken":{ + "shape":"NonEmptyString", + "documentation":"

    The next token used for paginated responses. When this is not empty, there are additional elements that the service has not included in this request. Use this token with the next request to retrieve additional objects.

    " + } + } + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["resourceArn"], + "members":{ + "resourceArn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource whose tags you want to retrieve.

    ", + "location":"uri", + "locationName":"resourceArn" + } + } + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "members":{ + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags for the specified resource.

    " + } + } + }, + "Logging":{ + "type":"structure", + "members":{ + "s3Logs":{ + "shape":"S3Logs", + "documentation":"

    The Amazon S3 logging configuration.

    " + } + }, + "documentation":"

    Logging configuration defines where Image Builder uploads your logs.

    " + }, + "NonEmptyString":{ + "type":"string", + "max":1024, + "min":1 + }, + "NullableBoolean":{"type":"boolean"}, + "OutputResources":{ + "type":"structure", + "members":{ + "amis":{ + "shape":"AmiList", + "documentation":"

    The EC2 AMIs created by this image.

    " + } + }, + "documentation":"

    The resources produced by this image.

    " + }, + "Ownership":{ + "type":"string", + "enum":[ + "Self", + "Shared", + "Amazon" + ] + }, + "PipelineExecutionStartCondition":{ + "type":"string", + "enum":[ + "EXPRESSION_MATCH_ONLY", + "EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE" + ] + }, + "PipelineStatus":{ + "type":"string", + "enum":[ + "DISABLED", + "ENABLED" + ] + }, + "Platform":{ + "type":"string", + "enum":[ + "Windows", + "Linux" + ] + }, + "PutComponentPolicyRequest":{ + "type":"structure", + "required":[ + "componentArn", + "policy" + ], + "members":{ + "componentArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component that this policy should be applied to.

    " + }, + "policy":{ + "shape":"ResourcePolicyDocument", + "documentation":"

    The policy to apply.

    " + } + } + }, + "PutComponentPolicyResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "componentArn":{ + "shape":"ComponentBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the component that this policy was applied to.

    " + } + } + }, + "PutImagePolicyRequest":{ + "type":"structure", + "required":[ + "imageArn", + "policy" + ], + "members":{ + "imageArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image that this policy should be applied to.

    " + }, + "policy":{ + "shape":"ResourcePolicyDocument", + "documentation":"

    The policy to apply.

    " + } + } + }, + "PutImagePolicyResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image that this policy was applied to.

    " + } + } + }, + "PutImageRecipePolicyRequest":{ + "type":"structure", + "required":[ + "imageRecipeArn", + "policy" + ], + "members":{ + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that this policy should be applied to.

    " + }, + "policy":{ + "shape":"ResourcePolicyDocument", + "documentation":"

    The policy to apply.

    " + } + } + }, + "PutImageRecipePolicyResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that this policy was applied to.

    " + } + } + }, + "ResourceAlreadyExistsException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The resource that you are trying to create already exists.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ResourceDependencyException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have attempted to mutate or delete a resource with a dependency that prohibits this action. See the error message for more details.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ResourceInUseException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The resource that you are trying to operate on is currently in use. Review the message details and retry later.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ResourceName":{ + "type":"string", + "pattern":"^[-_A-Za-z-0-9][-_A-Za-z0-9 ]{1,126}[-_A-Za-z-0-9]$" + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    At least one of the resources referenced by your request does not exist.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "ResourcePolicyDocument":{ + "type":"string", + "max":30000, + "min":1 + }, + "RestrictedInteger":{ + "type":"integer", + "max":25, + "min":1 + }, + "S3Logs":{ + "type":"structure", + "members":{ + "s3BucketName":{ + "shape":"NonEmptyString", + "documentation":"

    The Amazon S3 bucket in which to store the logs.

    " + }, + "s3KeyPrefix":{ + "shape":"NonEmptyString", + "documentation":"

    The Amazon S3 path in which to store the logs.

    " + } + }, + "documentation":"

    Amazon S3 logging configuration.

    " + }, + "Schedule":{ + "type":"structure", + "members":{ + "scheduleExpression":{ + "shape":"NonEmptyString", + "documentation":"

    The expression determines how often EC2 Image Builder evaluates your pipelineExecutionStartCondition.

    " + }, + "pipelineExecutionStartCondition":{ + "shape":"PipelineExecutionStartCondition", + "documentation":"

    The condition configures when the pipeline should trigger a new image build. When the pipelineExecutionStartCondition is set to EXPRESSION_MATCH_AND_DEPENDENCY_UPDATES_AVAILABLE, EC2 Image Builder will build a new image only when there are known changes pending. When it is set to EXPRESSION_MATCH_ONLY, it will build a new image every time the CRON expression matches the current time.

    " + } + }, + "documentation":"

    A schedule configures how often and when a pipeline will automatically create a new image.

    " + }, + "SecurityGroupIds":{ + "type":"list", + "member":{"shape":"NonEmptyString"} + }, + "ServiceException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    This exception is thrown when the service encounters an unrecoverable exception.

    ", + "error":{"httpStatusCode":500}, + "exception":true + }, + "ServiceUnavailableException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The service is unable to process your request at this time.

    ", + "error":{"httpStatusCode":503}, + "exception":true + }, + "SnsTopicArn":{ + "type":"string", + "pattern":"^arn:aws[^:]*:sns:[^:]+:\\d{12}:[a-zA-Z0-9-_]{1,256}$" + }, + "StartImagePipelineExecutionRequest":{ + "type":"structure", + "required":[ + "imagePipelineArn", + "clientToken" + ], + "members":{ + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that you want to manually invoke.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "StartImagePipelineExecutionResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "imageBuildVersionArn":{ + "shape":"ImageBuildVersionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image that was created by this request.

    " + } + } + }, + "StringList":{ + "type":"list", + "member":{"shape":"NonEmptyString"} + }, + "TagKey":{ + "type":"string", + "max":128, + "min":1, + "pattern":"^(?!aws:)[a-zA-Z+-=._:/]+$" + }, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"}, + "max":50, + "min":1 + }, + "TagMap":{ + "type":"map", + "key":{"shape":"TagKey"}, + "value":{"shape":"TagValue"}, + "max":50, + "min":1 + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tags" + ], + "members":{ + "resourceArn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource that you want to tag.

    ", + "location":"uri", + "locationName":"resourceArn" + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    The tags to apply to the resource.

    " + } + } + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{ + "type":"string", + "max":256 + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tagKeys" + ], + "members":{ + "resourceArn":{ + "shape":"ImageBuilderArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource that you want to untag.

    ", + "location":"uri", + "locationName":"resourceArn" + }, + "tagKeys":{ + "shape":"TagKeyList", + "documentation":"

    The tag keys to remove from the resource.

    ", + "location":"querystring", + "locationName":"tagKeys" + } + } + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "UpdateDistributionConfigurationRequest":{ + "type":"structure", + "required":[ + "distributionConfigurationArn", + "distributions", + "clientToken" + ], + "members":{ + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that you want to update.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the distribution configuration.

    " + }, + "distributions":{ + "shape":"DistributionList", + "documentation":"

    The distributions of the distribution configuration.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token of the distribution configuration.

    ", + "idempotencyToken":true + } + } + }, + "UpdateDistributionConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that was updated by this request.

    " + } + } + }, + "UpdateImagePipelineRequest":{ + "type":"structure", + "required":[ + "imagePipelineArn", + "imageRecipeArn", + "infrastructureConfigurationArn", + "clientToken" + ], + "members":{ + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that you want to update.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the image pipeline.

    " + }, + "imageRecipeArn":{ + "shape":"ImageRecipeArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image recipe that will be used to configure images updated by this image pipeline.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that will be used to build images updated by this image pipeline.

    " + }, + "distributionConfigurationArn":{ + "shape":"DistributionConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the distribution configuration that will be used to configure and distribute images updated by this image pipeline.

    " + }, + "imageTestsConfiguration":{ + "shape":"ImageTestsConfiguration", + "documentation":"

    The image test configuration of the image pipeline.

    " + }, + "schedule":{ + "shape":"Schedule", + "documentation":"

    The schedule of the image pipeline.

    " + }, + "status":{ + "shape":"PipelineStatus", + "documentation":"

    The status of the image pipeline.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "UpdateImagePipelineResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "imagePipelineArn":{ + "shape":"ImagePipelineArn", + "documentation":"

    The Amazon Resource Name (ARN) of the image pipeline that was updated by this request.

    " + } + } + }, + "UpdateInfrastructureConfigurationRequest":{ + "type":"structure", + "required":[ + "infrastructureConfigurationArn", + "instanceProfileName", + "clientToken" + ], + "members":{ + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that you want to update.

    " + }, + "description":{ + "shape":"NonEmptyString", + "documentation":"

    The description of the infrastructure configuration.

    " + }, + "instanceTypes":{ + "shape":"InstanceTypeList", + "documentation":"

    The instance types of the infrastructure configuration. You can specify one or more instance types to use for this build. The service will pick one of these instance types based on availability.

    " + }, + "instanceProfileName":{ + "shape":"NonEmptyString", + "documentation":"

    The instance profile to associate with the instance used to customize your EC2 AMI.

    " + }, + "securityGroupIds":{ + "shape":"SecurityGroupIds", + "documentation":"

    The security group IDs to associate with the instance used to customize your EC2 AMI.

    " + }, + "subnetId":{ + "shape":"NonEmptyString", + "documentation":"

    The subnet ID to place the instance used to customize your EC2 AMI in.

    " + }, + "logging":{ + "shape":"Logging", + "documentation":"

    The logging configuration of the infrastructure configuration.

    " + }, + "keyPair":{ + "shape":"NonEmptyString", + "documentation":"

    The key pair of the infrastructure configuration. This can be used to log on to and debug the instance used to create your image.

    " + }, + "terminateInstanceOnFailure":{ + "shape":"NullableBoolean", + "documentation":"

    The terminate instance on failure setting of the infrastructure configuration. Set to false if you want Image Builder to retain the instance used to configure your AMI if the build or test phase of your workflow fails.

    " + }, + "snsTopicArn":{ + "shape":"SnsTopicArn", + "documentation":"

    The SNS topic on which to send image build events.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    ", + "idempotencyToken":true + } + } + }, + "UpdateInfrastructureConfigurationResponse":{ + "type":"structure", + "members":{ + "requestId":{ + "shape":"NonEmptyString", + "documentation":"

    The request ID that uniquely identifies this request.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    The idempotency token used to make this request idempotent.

    " + }, + "infrastructureConfigurationArn":{ + "shape":"InfrastructureConfigurationArn", + "documentation":"

    The Amazon Resource Name (ARN) of the infrastructure configuration that was updated by this request.

    " + } + } + }, + "Uri":{"type":"string"}, + "VersionNumber":{ + "type":"string", + "pattern":"^[0-9]+\\.[0-9]+\\.[0-9]+$" + } + }, + "documentation":"

    EC2 Image Builder is a fully managed AWS service that makes it easier to automate the creation, management, and deployment of customized, secure, and up-to-date “golden” server images that are pre-installed and pre-configured with software and settings to meet specific IT standards.

    " +} diff --git a/services/inspector/build.properties b/services/inspector/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/inspector/build.properties +++ b/services/inspector/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/inspector/pom.xml b/services/inspector/pom.xml index a580d3a27cb0..1fac9dd35030 100644 --- a/services/inspector/pom.xml +++ b/services/inspector/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + iotsecuretunneling + AWS Java SDK :: Services :: IoTSecureTunneling + The AWS Java SDK for IoTSecureTunneling module holds the client classes that are used for + communicating with IoTSecureTunneling. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.iotsecuretunneling + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/iotsecuretunneling/src/main/resources/codegen-resources/paginators-1.json b/services/iotsecuretunneling/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..cd36b9c69a85 --- /dev/null +++ b/services/iotsecuretunneling/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "ListTunnels": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults" + } + } +} diff --git a/services/iotsecuretunneling/src/main/resources/codegen-resources/service-2.json b/services/iotsecuretunneling/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..effc6f03149e --- /dev/null +++ b/services/iotsecuretunneling/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,539 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2018-10-05", + "endpointPrefix":"api.tunneling.iot", + "jsonVersion":"1.1", + "protocol":"json", + "serviceFullName":"AWS IoT Secure Tunneling", + "serviceId":"IoTSecureTunneling", + "signatureVersion":"v4", + "signingName":"IoTSecuredTunneling", + "targetPrefix":"IoTSecuredTunneling", + "uid":"iotsecuretunneling-2018-10-05" + }, + "operations":{ + "CloseTunnel":{ + "name":"CloseTunnel", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CloseTunnelRequest"}, + "output":{"shape":"CloseTunnelResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Closes a tunnel identified by the unique tunnel id. When a CloseTunnel request is received, we close the WebSocket connections between the client and proxy server so no data can be transmitted.

    " + }, + "DescribeTunnel":{ + "name":"DescribeTunnel", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeTunnelRequest"}, + "output":{"shape":"DescribeTunnelResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Gets information about a tunnel identified by the unique tunnel id.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Lists the tags for the specified resource.

    " + }, + "ListTunnels":{ + "name":"ListTunnels", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListTunnelsRequest"}, + "output":{"shape":"ListTunnelsResponse"}, + "documentation":"

    List all tunnels for an AWS account. Tunnels are listed by creation time in descending order, newer tunnels will be listed before older tunnels.

    " + }, + "OpenTunnel":{ + "name":"OpenTunnel", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"OpenTunnelRequest"}, + "output":{"shape":"OpenTunnelResponse"}, + "errors":[ + {"shape":"LimitExceededException"} + ], + "documentation":"

    Creates a new tunnel, and returns two client access tokens for clients to use to connect to the AWS IoT Secure Tunneling proxy server. .

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    A resource tag.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Removes a tag from a resource.

    " + } + }, + "shapes":{ + "AmazonResourceName":{ + "type":"string", + "max":1011, + "min":1 + }, + "ClientAccessToken":{ + "type":"string", + "sensitive":true + }, + "CloseTunnelRequest":{ + "type":"structure", + "required":["tunnelId"], + "members":{ + "tunnelId":{ + "shape":"TunnelId", + "documentation":"

    The ID of the tunnel to close.

    " + }, + "delete":{ + "shape":"DeleteFlag", + "documentation":"

    When set to true, AWS IoT Secure Tunneling deletes the tunnel data immediately.

    ", + "box":true + } + } + }, + "CloseTunnelResponse":{ + "type":"structure", + "members":{ + } + }, + "ConnectionState":{ + "type":"structure", + "members":{ + "status":{ + "shape":"ConnectionStatus", + "documentation":"

    The connection status of the tunnel. Valid values are CONNECTED and DISCONNECTED.

    " + }, + "lastUpdatedAt":{ + "shape":"DateType", + "documentation":"

    The last time the connection status was updated.

    " + } + }, + "documentation":"

    The state of a connection.

    " + }, + "ConnectionStatus":{ + "type":"string", + "enum":[ + "CONNECTED", + "DISCONNECTED" + ] + }, + "DateType":{"type":"timestamp"}, + "DeleteFlag":{"type":"boolean"}, + "DescribeTunnelRequest":{ + "type":"structure", + "required":["tunnelId"], + "members":{ + "tunnelId":{ + "shape":"TunnelId", + "documentation":"

    The tunnel to describe.

    " + } + } + }, + "DescribeTunnelResponse":{ + "type":"structure", + "members":{ + "tunnel":{ + "shape":"Tunnel", + "documentation":"

    The tunnel being described.

    " + } + } + }, + "Description":{ + "type":"string", + "pattern":"[^\\p{C}]{1,2048}" + }, + "DestinationConfig":{ + "type":"structure", + "required":[ + "thingName", + "services" + ], + "members":{ + "thingName":{ + "shape":"ThingName", + "documentation":"

    The name of the IoT thing to which you want to connect.

    " + }, + "services":{ + "shape":"ServiceList", + "documentation":"

    A list of service names that identity the target application. Currently, you can only specify a single name. The AWS IoT client running on the destination device reads this value and uses it to look up a port or an IP address and a port. The AWS IoT client instantiates the local proxy which uses this information to connect to the destination application.

    " + } + }, + "documentation":"

    The destination configuration.

    " + }, + "ErrorMessage":{"type":"string"}, + "LimitExceededException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Thrown when a tunnel limit is exceeded.

    ", + "exception":true + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["resourceArn"], + "members":{ + "resourceArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The resource ARN.

    " + } + } + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "members":{ + "tags":{ + "shape":"TagList", + "documentation":"

    The tags for the specified resource.

    " + } + } + }, + "ListTunnelsRequest":{ + "type":"structure", + "members":{ + "thingName":{ + "shape":"ThingName", + "documentation":"

    The name of the IoT thing associated with the destination device.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return at once.

    ", + "box":true + }, + "nextToken":{ + "shape":"NextToken", + "documentation":"

    A token to retrieve the next set of results.

    " + } + } + }, + "ListTunnelsResponse":{ + "type":"structure", + "members":{ + "tunnelSummaries":{ + "shape":"TunnelSummaryList", + "documentation":"

    A short description of the tunnels in an AWS account.

    " + }, + "nextToken":{ + "shape":"NextToken", + "documentation":"

    A token to used to retrieve the next set of results.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":100, + "min":1 + }, + "NextToken":{ + "type":"string", + "pattern":"[a-zA-Z0-9_=-]{1,4096}" + }, + "OpenTunnelRequest":{ + "type":"structure", + "members":{ + "description":{ + "shape":"Description", + "documentation":"

    A short text description of the tunnel.

    " + }, + "tags":{ + "shape":"TagList", + "documentation":"

    A collection of tag metadata.

    " + }, + "destinationConfig":{ + "shape":"DestinationConfig", + "documentation":"

    The destination configuration for the OpenTunnel request.

    " + }, + "timeoutConfig":{ + "shape":"TimeoutConfig", + "documentation":"

    Timeout configuration for a tunnel.

    " + } + } + }, + "OpenTunnelResponse":{ + "type":"structure", + "members":{ + "tunnelId":{ + "shape":"TunnelId", + "documentation":"

    A unique alpha-numeric tunnel ID.

    " + }, + "tunnelArn":{ + "shape":"TunnelArn", + "documentation":"

    The Amazon Resource Name for the tunnel. The tunnel ARN format is arn:aws:tunnel:<region>:<account-id>:tunnel/<tunnel-id>

    " + }, + "sourceAccessToken":{ + "shape":"ClientAccessToken", + "documentation":"

    The access token the source local proxy uses to connect to AWS IoT Secure Tunneling.

    " + }, + "destinationAccessToken":{ + "shape":"ClientAccessToken", + "documentation":"

    The access token the destination local proxy uses to connect to AWS IoT Secure Tunneling.

    " + } + } + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Thrown when an operation is attempted on a resource that does not exist.

    ", + "exception":true + }, + "Service":{ + "type":"string", + "max":8, + "min":1, + "pattern":"[a-zA-Z0-9:_-]+" + }, + "ServiceList":{ + "type":"list", + "member":{"shape":"Service"}, + "max":1, + "min":1 + }, + "Tag":{ + "type":"structure", + "required":[ + "key", + "value" + ], + "members":{ + "key":{ + "shape":"TagKey", + "documentation":"

    The key of the tag.

    " + }, + "value":{ + "shape":"TagValue", + "documentation":"

    The value of the tag.

    " + } + }, + "documentation":"

    An arbitary key/value pair used to add searchable metadata to secure tunnel resources.

    " + }, + "TagKey":{ + "type":"string", + "max":128, + "min":1, + "pattern":"^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$" + }, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"}, + "max":200, + "min":0 + }, + "TagList":{ + "type":"list", + "member":{"shape":"Tag"}, + "max":200, + "min":1 + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tags" + ], + "members":{ + "resourceArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The ARN of the resource.

    " + }, + "tags":{ + "shape":"TagList", + "documentation":"

    The tags for the resource.

    " + } + } + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{ + "type":"string", + "max":256, + "min":0, + "pattern":"^([\\p{L}\\p{Z}\\p{N}_.:/=+\\-@]*)$" + }, + "ThingName":{ + "type":"string", + "max":128, + "min":1, + "pattern":"[a-zA-Z0-9:_-]+" + }, + "TimeoutConfig":{ + "type":"structure", + "members":{ + "maxLifetimeTimeoutMinutes":{ + "shape":"TimeoutInMin", + "documentation":"

    The maximum amount of time (in minutes) a tunnel can remain open. If not specified, maxLifetimeTimeoutMinutes defaults to 720 minutes. Valid values are from 1 minute to 12 hours (720 minutes)

    ", + "box":true + } + }, + "documentation":"

    Tunnel timeout configuration.

    " + }, + "TimeoutInMin":{ + "type":"integer", + "max":720, + "min":1 + }, + "Tunnel":{ + "type":"structure", + "members":{ + "tunnelId":{ + "shape":"TunnelId", + "documentation":"

    A unique alpha-numeric ID that identifies a tunnel.

    " + }, + "tunnelArn":{ + "shape":"TunnelArn", + "documentation":"

    The Amazon Resource Name (ARN) of a tunnel. The tunnel ARN format is arn:aws:tunnel:<region>:<account-id>:tunnel/<tunnel-id>

    " + }, + "status":{ + "shape":"TunnelStatus", + "documentation":"

    The status of a tunnel. Valid values are: Open and Closed.

    " + }, + "sourceConnectionState":{ + "shape":"ConnectionState", + "documentation":"

    The connection state of the source application.

    " + }, + "destinationConnectionState":{ + "shape":"ConnectionState", + "documentation":"

    The connection state of the destination application.

    " + }, + "description":{ + "shape":"Description", + "documentation":"

    A description of the tunnel.

    " + }, + "destinationConfig":{ + "shape":"DestinationConfig", + "documentation":"

    The destination configuration that specifies the thing name of the destination device and a service name that the local proxy uses to connect to the destination application.

    " + }, + "timeoutConfig":{ + "shape":"TimeoutConfig", + "documentation":"

    Timeout configuration for the tunnel.

    " + }, + "tags":{ + "shape":"TagList", + "documentation":"

    A list of tag metadata associated with the secure tunnel.

    " + }, + "createdAt":{ + "shape":"DateType", + "documentation":"

    The time when the tunnel was created.

    " + }, + "lastUpdatedAt":{ + "shape":"DateType", + "documentation":"

    The last time the tunnel was updated.

    " + } + }, + "documentation":"

    A connection between a source computer and a destination device.

    " + }, + "TunnelArn":{ + "type":"string", + "max":1600, + "min":1 + }, + "TunnelId":{ + "type":"string", + "pattern":"[a-zA-Z0-9_\\-+=:]{1,128}" + }, + "TunnelStatus":{ + "type":"string", + "enum":[ + "OPEN", + "CLOSED" + ] + }, + "TunnelSummary":{ + "type":"structure", + "members":{ + "tunnelId":{ + "shape":"TunnelId", + "documentation":"

    The unique alpha-numeric identifier for the tunnel.

    " + }, + "tunnelArn":{ + "shape":"TunnelArn", + "documentation":"

    The Amazon Resource Name of the tunnel. The tunnel ARN format is arn:aws:tunnel:<region>:<account-id>:tunnel/<tunnel-id>

    " + }, + "status":{ + "shape":"TunnelStatus", + "documentation":"

    The status of a tunnel. Valid values are: Open and Closed.

    " + }, + "description":{ + "shape":"Description", + "documentation":"

    A description of the tunnel.

    " + }, + "createdAt":{ + "shape":"DateType", + "documentation":"

    The time the tunnel was created.

    " + }, + "lastUpdatedAt":{ + "shape":"DateType", + "documentation":"

    The time the tunnel was last updated.

    " + } + }, + "documentation":"

    Information about the tunnel.

    " + }, + "TunnelSummaryList":{ + "type":"list", + "member":{"shape":"TunnelSummary"} + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tagKeys" + ], + "members":{ + "resourceArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The resource ARN.

    " + }, + "tagKeys":{ + "shape":"TagKeyList", + "documentation":"

    The keys of the tags to remove.

    " + } + } + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + } + } + }, + "documentation":"AWS IoT Secure Tunneling

    AWS IoT Secure Tunnling enables you to create remote connections to devices deployed in the field.

    For more information about how AWS IoT Secure Tunneling works, see the User Guide.

    " +} diff --git a/services/iotthingsgraph/pom.xml b/services/iotthingsgraph/pom.xml index 0160519dc901..81536ee4da43 100644 --- a/services/iotthingsgraph/pom.xml +++ b/services/iotthingsgraph/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + kendra + AWS Java SDK :: Services :: Kendra + The AWS Java SDK for Kendra module holds the client classes that are used for + communicating with Kendra. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.kendra + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/kendra/src/main/resources/codegen-resources/paginators-1.json b/services/kendra/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..2170bfc57ce1 --- /dev/null +++ b/services/kendra/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,19 @@ +{ + "pagination": { + "ListDataSourceSyncJobs": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListDataSources": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListIndices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/kendra/src/main/resources/codegen-resources/service-2.json b/services/kendra/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..889b81c83d4d --- /dev/null +++ b/services/kendra/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,2598 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-02-03", + "endpointPrefix":"kendra", + "jsonVersion":"1.1", + "protocol":"json", + "serviceAbbreviation":"kendra", + "serviceFullName":"AWSKendraFrontendService", + "serviceId":"kendra", + "signatureVersion":"v4", + "signingName":"kendra", + "targetPrefix":"AWSKendraFrontendService", + "uid":"kendra-2019-02-03" + }, + "operations":{ + "BatchDeleteDocument":{ + "name":"BatchDeleteDocument", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"BatchDeleteDocumentRequest"}, + "output":{"shape":"BatchDeleteDocumentResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Removes one or more documents from an index. The documents must have been added with the BatchPutDocument operation.

    The documents are deleted asynchronously. You can see the progress of the deletion by using AWS CloudWatch. Any error messages releated to the processing of the batch are sent to you CloudWatch log.

    " + }, + "BatchPutDocument":{ + "name":"BatchPutDocument", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"BatchPutDocumentRequest"}, + "output":{"shape":"BatchPutDocumentResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Adds one or more documents to an index.

    The BatchPutDocument operation enables you to ingest inline documents or a set of documents stored in an Amazon S3 bucket. Use this operation to ingest your text and unstructured text into an index, add custom attributes to the documents, and to attach an access control list to the documents added to the index.

    The documents are indexed asynchronously. You can see the progress of the batch using AWS CloudWatch. Any error messages related to processing the batch are sent to your AWS CloudWatch log.

    " + }, + "CreateDataSource":{ + "name":"CreateDataSource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateDataSourceRequest"}, + "output":{"shape":"CreateDataSourceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ResourceAlreadyExistException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates a data source that you use to with an Amazon Kendra index.

    You specify a name, connector type and description for your data source. You can choose between an S3 connector, a SharePoint Online connector, and a database connector.

    You also specify configuration information such as document metadata (author, source URI, and so on) and user context information.

    CreateDataSource is a synchronous operation. The operation returns 200 if the data source was successfully created. Otherwise, an exception is raised.

    " + }, + "CreateFaq":{ + "name":"CreateFaq", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateFaqRequest"}, + "output":{"shape":"CreateFaqResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates an new set of frequently asked question (FAQ) questions and answers.

    " + }, + "CreateIndex":{ + "name":"CreateIndex", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateIndexRequest"}, + "output":{"shape":"CreateIndexResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceAlreadyExistException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ConflictException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates a new Amazon Kendra index. Index creation is an asynchronous operation. To determine if index creation has completed, check the Status field returned from a call to . The Status field is set to ACTIVE when the index is ready to use.

    Once the index is active you can index your documents using the operation or using one of the supported data sources.

    " + }, + "DeleteFaq":{ + "name":"DeleteFaq", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteFaqRequest"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Removes an FAQ from an index.

    " + }, + "DeleteIndex":{ + "name":"DeleteIndex", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteIndexRequest"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes an existing Amazon Kendra index. An exception is not thrown if the index is already being deleted. While the index is being deleted, the Status field returned by a call to the DescribeIndex operation is set to DELETING.

    " + }, + "DescribeDataSource":{ + "name":"DescribeDataSource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeDataSourceRequest"}, + "output":{"shape":"DescribeDataSourceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about a Amazon Kendra data source.

    " + }, + "DescribeFaq":{ + "name":"DescribeFaq", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeFaqRequest"}, + "output":{"shape":"DescribeFaqResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about an FAQ list.

    " + }, + "DescribeIndex":{ + "name":"DescribeIndex", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeIndexRequest"}, + "output":{"shape":"DescribeIndexResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Describes an existing Amazon Kendra index

    " + }, + "ListDataSourceSyncJobs":{ + "name":"ListDataSourceSyncJobs", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListDataSourceSyncJobsRequest"}, + "output":{"shape":"ListDataSourceSyncJobsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ConflictException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets statistics about synchronizing Amazon Kendra with a data source.

    " + }, + "ListDataSources":{ + "name":"ListDataSources", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListDataSourcesRequest"}, + "output":{"shape":"ListDataSourcesResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Lists the data sources that you have created.

    " + }, + "ListFaqs":{ + "name":"ListFaqs", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListFaqsRequest"}, + "output":{"shape":"ListFaqsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets a list of FAQ lists associated with an index.

    " + }, + "ListIndices":{ + "name":"ListIndices", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListIndicesRequest"}, + "output":{"shape":"ListIndicesResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Lists the Amazon Kendra indexes that you have created.

    " + }, + "Query":{ + "name":"Query", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"QueryRequest"}, + "output":{"shape":"QueryResult"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Searches an active index. Use this API to search your documents using query. The Query operation enables to do faceted search and to filter results based on document attributes.

    It also enables you to provide user context that Amazon Kendra uses to enforce document access control in the search results.

    Amazon Kendra searches your index for text content and question and answer (FAQ) content. By default the response contains three types of results.

    • Relevant passages

    • Matching FAQs

    • Relevant documents

    You can specify that the query return only one type of result using the QueryResultTypeConfig parameter.

    " + }, + "StartDataSourceSyncJob":{ + "name":"StartDataSourceSyncJob", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"StartDataSourceSyncJobRequest"}, + "output":{"shape":"StartDataSourceSyncJobResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ConflictException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Starts a synchronization job for a data source. If a synchronization job is already in progress, Amazon Kendra returns a ResourceInUseException exception.

    " + }, + "StopDataSourceSyncJob":{ + "name":"StopDataSourceSyncJob", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"StopDataSourceSyncJobRequest"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Stops a running synchronization job. You can't stop a scheduled synchronization job.

    " + }, + "SubmitFeedback":{ + "name":"SubmitFeedback", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"SubmitFeedbackRequest"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceUnavailableException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Enables you to provide feedback to Amazon Kendra to improve the performance of the service.

    " + }, + "UpdateDataSource":{ + "name":"UpdateDataSource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateDataSourceRequest"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates an existing Amazon Kendra data source.

    " + }, + "UpdateIndex":{ + "name":"UpdateIndex", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateIndexRequest"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ConflictException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates an existing Amazon Kendra index.

    " + } + }, + "shapes":{ + "AccessControlListConfiguration":{ + "type":"structure", + "members":{ + "KeyPath":{ + "shape":"S3ObjectKey", + "documentation":"

    Path to the AWS S3 bucket that contains the ACL files.

    " + } + }, + "documentation":"

    Access Control List files for the documents in a data source.

    " + }, + "AccessDeniedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "AclConfiguration":{ + "type":"structure", + "required":["AllowedGroupsColumnName"], + "members":{ + "AllowedGroupsColumnName":{ + "shape":"ColumnName", + "documentation":"

    A list of groups, separated by semi-colons, that filters a query response based on user context. The document is only returned to users that are in one of the groups specified in the UserContext field of the Query operation.

    " + } + }, + "documentation":"

    Provides information about the column that should be used for filtering the query response by groups.

    " + }, + "AdditionalResultAttribute":{ + "type":"structure", + "required":[ + "Key", + "ValueType", + "Value" + ], + "members":{ + "Key":{ + "shape":"String", + "documentation":"

    " + }, + "ValueType":{ + "shape":"AdditionalResultAttributeValueType", + "documentation":"

    " + }, + "Value":{ + "shape":"AdditionalResultAttributeValue", + "documentation":"

    " + } + }, + "documentation":"

    " + }, + "AdditionalResultAttributeList":{ + "type":"list", + "member":{"shape":"AdditionalResultAttribute"} + }, + "AdditionalResultAttributeValue":{ + "type":"structure", + "members":{ + "TextWithHighlightsValue":{ + "shape":"TextWithHighlights", + "documentation":"

    The text associated with the attribute and information about the highlight to apply to the text.

    " + } + }, + "documentation":"

    An attribute returned with a document from a search.

    " + }, + "AdditionalResultAttributeValueType":{ + "type":"string", + "enum":["TEXT_WITH_HIGHLIGHTS_VALUE"] + }, + "AttributeFilter":{ + "type":"structure", + "members":{ + "AndAllFilters":{ + "shape":"AttributeFilterList", + "documentation":"

    Performs a logical AND operation on all supplied filters.

    " + }, + "OrAllFilters":{ + "shape":"AttributeFilterList", + "documentation":"

    Performs a logical OR operation on all supplied filters.

    " + }, + "NotFilter":{ + "shape":"AttributeFilter", + "documentation":"

    Performs a logical NOT operation on all supplied filters.

    " + }, + "EqualsTo":{ + "shape":"DocumentAttribute", + "documentation":"

    Performs an equals operation on two document attributes.

    " + }, + "ContainsAll":{ + "shape":"DocumentAttribute", + "documentation":"

    Returns true when a document contains all of the specified document attributes.

    " + }, + "ContainsAny":{ + "shape":"DocumentAttribute", + "documentation":"

    Returns true when a document contains any of the specified document attributes.

    " + }, + "GreaterThan":{ + "shape":"DocumentAttribute", + "documentation":"

    Performs a greater than operation on two document attributes. Use with a document attribute of type Integer or Long.

    " + }, + "GreaterThanOrEquals":{ + "shape":"DocumentAttribute", + "documentation":"

    Performs a greater or equals than operation on two document attributes. Use with a document attribute of type Integer or Long.

    " + }, + "LessThan":{ + "shape":"DocumentAttribute", + "documentation":"

    Performs a less than operation on two document attributes. Use with a document attribute of type Integer or Long.

    " + }, + "LessThanOrEquals":{ + "shape":"DocumentAttribute", + "documentation":"

    Performs a less than or equals operation on two document attributes. Use with a document attribute of type Integer or Long.

    " + } + }, + "documentation":"

    Provides filtering the query results based on document attributes.

    When you use the AndAllFilters or OrAllFilters, filters you can use a total of 3 layers. For example, you can use:

    1. <AndAllFilters>

    2. <OrAllFilters>

    3. <EqualTo>

    " + }, + "AttributeFilterList":{ + "type":"list", + "member":{"shape":"AttributeFilter"}, + "max":5, + "min":1 + }, + "BatchDeleteDocumentRequest":{ + "type":"structure", + "required":[ + "IndexId", + "DocumentIdList" + ], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the documents to delete.

    " + }, + "DocumentIdList":{ + "shape":"DocumentIdList", + "documentation":"

    One or more identifiers for documents to delete from the index.

    " + } + } + }, + "BatchDeleteDocumentResponse":{ + "type":"structure", + "members":{ + "FailedDocuments":{ + "shape":"BatchDeleteDocumentResponseFailedDocuments", + "documentation":"

    A list of documents that could not be removed from the index. Each entry contains an error message that indicates why the document couldn't be removed from the index.

    " + } + } + }, + "BatchDeleteDocumentResponseFailedDocument":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"DocumentId", + "documentation":"

    The identifier of the document that couldn't be removed from the index.

    " + }, + "ErrorCode":{ + "shape":"ErrorCode", + "documentation":"

    The error code for why the document couldn't be removed from the index.

    " + }, + "ErrorMessage":{ + "shape":"ErrorMessage", + "documentation":"

    An explanation for why the document couldn't be removed from the index.

    " + } + }, + "documentation":"

    Provides information about documents that could not be removed from an index by the BatchDeleteDocument operation.

    " + }, + "BatchDeleteDocumentResponseFailedDocuments":{ + "type":"list", + "member":{"shape":"BatchDeleteDocumentResponseFailedDocument"} + }, + "BatchPutDocumentRequest":{ + "type":"structure", + "required":[ + "IndexId", + "Documents" + ], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index to add the documents to. You need to create the index first using the CreateIndex operation.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of a role that is allowed to run the BatchPutDocument operation. For more information, see IAM Roles for Amazon Kendra.

    " + }, + "Documents":{ + "shape":"DocumentList", + "documentation":"

    One or more documents to add to the index.

    Each document is limited to 5 Mb, the total size of the list is limited to 50 Mb.

    " + } + } + }, + "BatchPutDocumentResponse":{ + "type":"structure", + "members":{ + "FailedDocuments":{ + "shape":"BatchPutDocumentResponseFailedDocuments", + "documentation":"

    A list of documents that were not added to the index because the document failed a validation check. Each document contains an error message that indicates why the document couldn't be added to the index.

    If there was an error adding a document to an index the error is reported in your AWS CloudWatch log.

    " + } + } + }, + "BatchPutDocumentResponseFailedDocument":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"DocumentId", + "documentation":"

    The unique identifier of the document.

    " + }, + "ErrorCode":{ + "shape":"ErrorCode", + "documentation":"

    The type of error that caused the document to fail to be indexed.

    " + }, + "ErrorMessage":{ + "shape":"ErrorMessage", + "documentation":"

    A description of the reason why the document could not be indexed.

    " + } + }, + "documentation":"

    Provides information about a document that could not be indexed.

    " + }, + "BatchPutDocumentResponseFailedDocuments":{ + "type":"list", + "member":{"shape":"BatchPutDocumentResponseFailedDocument"} + }, + "Blob":{ + "type":"blob", + "max":153600, + "min":1 + }, + "Boolean":{"type":"boolean"}, + "ChangeDetectingColumns":{ + "type":"list", + "member":{"shape":"ColumnName"}, + "max":5, + "min":1 + }, + "ClickFeedback":{ + "type":"structure", + "required":[ + "ResultId", + "ClickTime" + ], + "members":{ + "ResultId":{ + "shape":"ResultId", + "documentation":"

    The unique identifier of the search result that was clicked.

    " + }, + "ClickTime":{ + "shape":"Timestamp", + "documentation":"

    The Unix timestamp of the data and time that the result was clicked.

    " + } + }, + "documentation":"

    Gathers information about when a particular result was clicked by a user. Your application uses the SubmitFeedback operation to provide click information.

    " + }, + "ClickFeedbackList":{ + "type":"list", + "member":{"shape":"ClickFeedback"} + }, + "ClientTokenName":{ + "type":"string", + "max":100, + "min":1 + }, + "ColumnConfiguration":{ + "type":"structure", + "required":[ + "DocumentIdColumnName", + "DocumentDataColumnName", + "ChangeDetectingColumns" + ], + "members":{ + "DocumentIdColumnName":{ + "shape":"ColumnName", + "documentation":"

    The column that provides the document's unique identifier.

    " + }, + "DocumentDataColumnName":{ + "shape":"ColumnName", + "documentation":"

    The column that contains the contents of the document.

    " + }, + "DocumentTitleColumnName":{ + "shape":"ColumnName", + "documentation":"

    The column that contains the title of the document.

    " + }, + "FieldMappings":{ + "shape":"DataSourceToIndexFieldMappingList", + "documentation":"

    An array of objects that map database column names to the corresponding fields in an index. You must first create the fields in the index using the UpdateIndex operation.

    " + }, + "ChangeDetectingColumns":{ + "shape":"ChangeDetectingColumns", + "documentation":"

    One to five columns that indicate when a document in the database has changed.

    " + } + }, + "documentation":"

    Provides information about how Amazon Kendra should use the columns of a database in an index.

    " + }, + "ColumnName":{ + "type":"string", + "max":100, + "min":1, + "pattern":"^[a-zA-Z][a-zA-Z0-9_]*$" + }, + "ConflictException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "ConnectionConfiguration":{ + "type":"structure", + "required":[ + "DatabaseHost", + "DatabasePort", + "DatabaseName", + "TableName", + "SecretArn" + ], + "members":{ + "DatabaseHost":{ + "shape":"DatabaseHost", + "documentation":"

    The name of the host for the database. Can be either a string (host.subdomain.domain.tld) or an IPv4 or IPv6 address.

    " + }, + "DatabasePort":{ + "shape":"DatabasePort", + "documentation":"

    The port that the database uses for connections.

    " + }, + "DatabaseName":{ + "shape":"DatabaseName", + "documentation":"

    The name of the database containing the document data.

    " + }, + "TableName":{ + "shape":"TableName", + "documentation":"

    The name of the table that contains the document data.

    " + }, + "SecretArn":{ + "shape":"SecretArn", + "documentation":"

    The Amazon Resource Name (ARN) of credentials stored in AWS Secrets Manager. The credentials should be a user/password pair. For more information, see Using a Database Data Source. For more information about AWS Secrets Manager, see What Is AWS Secrets Manager in the AWS Secrets Manager user guide.

    " + } + }, + "documentation":"

    Provides the information necessary to connect to a database.

    " + }, + "ContentType":{ + "type":"string", + "enum":[ + "PDF", + "HTML", + "MS_WORD", + "PLAIN_TEXT", + "PPT" + ] + }, + "CreateDataSourceRequest":{ + "type":"structure", + "required":[ + "Name", + "IndexId", + "Type", + "Configuration", + "RoleArn" + ], + "members":{ + "Name":{ + "shape":"DataSourceName", + "documentation":"

    A unique name for the data source. A data source name can't be changed without deleting and recreating the data source.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that should be associated with this data source.

    " + }, + "Type":{ + "shape":"DataSourceType", + "documentation":"

    The type of repository that contains the data source.

    " + }, + "Configuration":{ + "shape":"DataSourceConfiguration", + "documentation":"

    The connector configuration information that is required to access the repository.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description for the data source.

    " + }, + "Schedule":{ + "shape":"ScanSchedule", + "documentation":"

    Sets the frequency that Amazon Kendra will check the documents in your repository and update the index. If you don't set a schedule Amazon Kendra will not periodically update the index. You can call the StartDataSourceSyncJob operation to update the index.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of a role with permission to access the data source. For more information, see IAM Roles for Amazon Kendra.

    " + } + } + }, + "CreateDataSourceResponse":{ + "type":"structure", + "required":["Id"], + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    A unique identifier for the data source.

    " + } + } + }, + "CreateFaqRequest":{ + "type":"structure", + "required":[ + "IndexId", + "Name", + "S3Path", + "RoleArn" + ], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the FAQ.

    " + }, + "Name":{ + "shape":"FaqName", + "documentation":"

    The name that should be associated with the FAQ.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description of the FAQ.

    " + }, + "S3Path":{ + "shape":"S3Path", + "documentation":"

    The S3 location of the FAQ input data.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of a role with permission to access the S3 bucket that contains the FAQs. For more information, see IAM Roles for Amazon Kendra.

    " + } + } + }, + "CreateFaqResponse":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"FaqId", + "documentation":"

    The unique identifier of the FAQ.

    " + } + } + }, + "CreateIndexRequest":{ + "type":"structure", + "required":[ + "Name", + "RoleArn" + ], + "members":{ + "Name":{ + "shape":"IndexName", + "documentation":"

    The name for the new index.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    An IAM role that gives Amazon Kendra permissions to access your Amazon CloudWatch logs and metrics. This is also the role used when you use the BatchPutDocument operation to index documents from an Amazon S3 bucket.

    " + }, + "ServerSideEncryptionConfiguration":{ + "shape":"ServerSideEncryptionConfiguration", + "documentation":"

    The identifier of the AWS KMS customer managed key (CMK) to use to encrypt data indexed by Amazon Kendra. Amazon Kendra doesn't support asymmetric CMKs.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A description for the index.

    " + }, + "ClientToken":{ + "shape":"ClientTokenName", + "documentation":"

    A token that you provide to identify the request to create an index. Multiple calls to the CreateIndex operation with the same client token will create only one index.”

    ", + "idempotencyToken":true + } + } + }, + "CreateIndexResponse":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"IndexId", + "documentation":"

    The unique identifier of the index. Use this identifier when you query an index, set up a data source, or index a document.

    " + } + } + }, + "DataSourceConfiguration":{ + "type":"structure", + "members":{ + "S3Configuration":{ + "shape":"S3DataSourceConfiguration", + "documentation":"

    Provides information to create a connector for a document repository in an Amazon S3 bucket.

    " + }, + "SharePointConfiguration":{ + "shape":"SharePointConfiguration", + "documentation":"

    Provides information necessary to create a connector for a Microsoft SharePoint site.

    " + }, + "DatabaseConfiguration":{ + "shape":"DatabaseConfiguration", + "documentation":"

    Provides information necessary to create a connector for a database.

    " + } + }, + "documentation":"

    Configuration information for a Amazon Kendra data source.

    " + }, + "DataSourceDateFieldFormat":{ + "type":"string", + "max":40, + "min":4 + }, + "DataSourceFieldName":{ + "type":"string", + "max":100, + "min":1, + "pattern":"^[a-zA-Z][a-zA-Z0-9_.]*$" + }, + "DataSourceId":{ + "type":"string", + "max":100, + "min":1, + "pattern":"[a-zA-Z0-9][a-zA-Z0-9_-]*" + }, + "DataSourceInclusionsExclusionsStrings":{ + "type":"list", + "member":{"shape":"DataSourceInclusionsExclusionsStringsMember"}, + "max":100, + "min":0 + }, + "DataSourceInclusionsExclusionsStringsMember":{ + "type":"string", + "max":50, + "min":1 + }, + "DataSourceName":{ + "type":"string", + "max":1000, + "min":1, + "pattern":"[a-zA-Z0-9][a-zA-Z0-9_-]*" + }, + "DataSourceStatus":{ + "type":"string", + "enum":[ + "CREATING", + "DELETING", + "FAILED", + "UPDATING", + "ACTIVE" + ] + }, + "DataSourceSummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"DataSourceName", + "documentation":"

    The name of the data source.

    " + }, + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The unique identifier for the data source.

    " + }, + "Type":{ + "shape":"DataSourceType", + "documentation":"

    The type of the data source.

    " + }, + "CreatedAt":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime that the data source was created.

    " + }, + "UpdatedAt":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime that the data source was lasted updated.

    " + }, + "Status":{ + "shape":"DataSourceStatus", + "documentation":"

    The status of the data source. When the status is ATIVE the data source is ready to use.

    " + } + }, + "documentation":"

    Summary information for a Amazon Kendra data source. Returned in a call to .

    " + }, + "DataSourceSummaryList":{ + "type":"list", + "member":{"shape":"DataSourceSummary"} + }, + "DataSourceSyncJob":{ + "type":"structure", + "members":{ + "ExecutionId":{ + "shape":"String", + "documentation":"

    A unique identifier for the synchronization job.

    " + }, + "StartTime":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime that the synchronization job was started.

    " + }, + "EndTime":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime that the synchronization job was completed.

    " + }, + "Status":{ + "shape":"DataSourceSyncJobStatus", + "documentation":"

    The execution status of the synchronization job. When the Status field is set to SUCCEEDED, the synchronization job is done. If the status code is set to FAILED, the ErrorCode and ErrorMessage fields give you the reason for the failure.

    " + }, + "ErrorMessage":{ + "shape":"ErrorMessage", + "documentation":"

    If the Status field is set to ERROR, the ErrorMessage field contains a description of the error that caused the synchronization to fail.

    " + }, + "ErrorCode":{ + "shape":"ErrorCode", + "documentation":"

    If the Status field is set to FAILED, the ErrorCode field contains a the reason that the synchronization failed.

    " + }, + "DataSourceErrorCode":{ + "shape":"String", + "documentation":"

    If the reason that the synchronization failed is due to an error with the underlying data source, this field contains a code that identifies the error.

    " + } + }, + "documentation":"

    Provides information about a synchronization job.

    " + }, + "DataSourceSyncJobHistoryList":{ + "type":"list", + "member":{"shape":"DataSourceSyncJob"} + }, + "DataSourceSyncJobStatus":{ + "type":"string", + "enum":[ + "FAILED", + "SUCCEEDED", + "SYNCING", + "INCOMPLETE", + "STOPPING", + "ABORTED" + ] + }, + "DataSourceToIndexFieldMapping":{ + "type":"structure", + "required":[ + "DataSourceFieldName", + "IndexFieldName" + ], + "members":{ + "DataSourceFieldName":{ + "shape":"DataSourceFieldName", + "documentation":"

    The name of the column or attribute in the data source.

    " + }, + "DateFieldFormat":{ + "shape":"DataSourceDateFieldFormat", + "documentation":"

    The type of data stored in the column or attribute.

    " + }, + "IndexFieldName":{ + "shape":"IndexFieldName", + "documentation":"

    The name of the field in the index.

    " + } + }, + "documentation":"

    Maps a column or attribute in the data source to an index field. You must first create the fields in the index using the UpdateIndex operation.

    " + }, + "DataSourceToIndexFieldMappingList":{ + "type":"list", + "member":{"shape":"DataSourceToIndexFieldMapping"}, + "max":100, + "min":1 + }, + "DataSourceType":{ + "type":"string", + "enum":[ + "S3", + "SHAREPOINT", + "DATABASE" + ] + }, + "DataSourceVpcConfiguration":{ + "type":"structure", + "required":[ + "SubnetIds", + "SecurityGroupIds" + ], + "members":{ + "SubnetIds":{ + "shape":"SubnetIdList", + "documentation":"

    A list of identifiers for subnets within your Amazon VPC. The subnets should be able to connect to each other in the VPC, and they should have outgoing access to the Internet through a NAT device.

    " + }, + "SecurityGroupIds":{ + "shape":"SecurityGroupIdList", + "documentation":"

    A list of identifiers of security groups within your Amazon VPC. The security groups should enable Amazon Kendra to connect to the data source.

    " + } + }, + "documentation":"

    Provides information for connecting to an Amazon VPC.

    " + }, + "DatabaseConfiguration":{ + "type":"structure", + "required":[ + "DatabaseEngineType", + "ConnectionConfiguration", + "ColumnConfiguration" + ], + "members":{ + "DatabaseEngineType":{ + "shape":"DatabaseEngineType", + "documentation":"

    The type of database engine that runs the database.

    " + }, + "ConnectionConfiguration":{ + "shape":"ConnectionConfiguration", + "documentation":"

    The information necessary to connect to a database.

    " + }, + "VpcConfiguration":{"shape":"DataSourceVpcConfiguration"}, + "ColumnConfiguration":{ + "shape":"ColumnConfiguration", + "documentation":"

    Information about where the index should get the document information from the database.

    " + }, + "AclConfiguration":{ + "shape":"AclConfiguration", + "documentation":"

    Information about the database column that provides information for user context filtering.

    " + } + }, + "documentation":"

    Provides the information necessary to connect a database to an index.

    " + }, + "DatabaseEngineType":{ + "type":"string", + "enum":[ + "RDS_AURORA_MYSQL", + "RDS_AURORA_POSTGRESQL", + "RDS_MYSQL", + "RDS_POSTGRESQL" + ] + }, + "DatabaseHost":{ + "type":"string", + "max":253, + "min":1 + }, + "DatabaseName":{ + "type":"string", + "max":100, + "min":1, + "pattern":"^[a-zA-Z][a-zA-Z0-9_]*$" + }, + "DatabasePort":{ + "type":"integer", + "max":65535, + "min":1 + }, + "DeleteFaqRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"FaqId", + "documentation":"

    The identifier of the FAQ to remove.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The index to remove the FAQ from.

    " + } + } + }, + "DeleteIndexRequest":{ + "type":"structure", + "required":["Id"], + "members":{ + "Id":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index to delete.

    " + } + } + }, + "DescribeDataSourceRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The unique identifier of the data source to describe.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source.

    " + } + } + }, + "DescribeDataSourceResponse":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The identifier of the data source.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source.

    " + }, + "Name":{ + "shape":"DataSourceName", + "documentation":"

    The name that you gave the data source when it was created.

    " + }, + "Type":{ + "shape":"DataSourceType", + "documentation":"

    The type of the data source.

    " + }, + "Configuration":{ + "shape":"DataSourceConfiguration", + "documentation":"

    Information that describes where the data source is located and how the data source is configured. The specific information in the description depends on the data source provider.

    " + }, + "CreatedAt":{ + "shape":"Timestamp", + "documentation":"

    The Unix timestamp of when the data source was created.

    " + }, + "UpdatedAt":{ + "shape":"Timestamp", + "documentation":"

    The Unix timestamp of when the data source was last updated.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the data source.

    " + }, + "Status":{ + "shape":"DataSourceStatus", + "documentation":"

    The current status of the data source. When the status is ACTIVE the data source is ready to use. When the status is FAILED, the ErrorMessage field contains the reason that the data source failed.

    " + }, + "Schedule":{ + "shape":"ScanSchedule", + "documentation":"

    The schedule that Amazon Kendra will update the data source.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the role that enables the data source to access its resources.

    " + }, + "ErrorMessage":{ + "shape":"ErrorMessage", + "documentation":"

    When the Status field value is FAILED, the ErrorMessage field contains a description of the error that caused the data source to fail.

    " + } + } + }, + "DescribeFaqRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"FaqId", + "documentation":"

    The unique identifier of the FAQ.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the FAQ.

    " + } + } + }, + "DescribeFaqResponse":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"FaqId", + "documentation":"

    The identifier of the FAQ.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the FAQ.

    " + }, + "Name":{ + "shape":"FaqName", + "documentation":"

    The name that you gave the FAQ when it was created.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the FAQ that you provided when it was created.

    " + }, + "CreatedAt":{ + "shape":"Timestamp", + "documentation":"

    The date and time that the FAQ was created.

    " + }, + "UpdatedAt":{ + "shape":"Timestamp", + "documentation":"

    The date and time that the FAQ was last updated.

    " + }, + "S3Path":{"shape":"S3Path"}, + "Status":{ + "shape":"FaqStatus", + "documentation":"

    The status of the FAQ. It is ready to use when the status is ACTIVE.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the role that provides access to the S3 bucket containing the input files for the FAQ.

    " + }, + "ErrorMessage":{ + "shape":"ErrorMessage", + "documentation":"

    If the Status field is FAILED, the ErrorMessage field contains the reason why the FAQ failed.

    " + } + } + }, + "DescribeIndexRequest":{ + "type":"structure", + "required":["Id"], + "members":{ + "Id":{ + "shape":"IndexId", + "documentation":"

    The name of the index to describe.

    " + } + } + }, + "DescribeIndexResponse":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"IndexName", + "documentation":"

    The name of the index.

    " + }, + "Id":{ + "shape":"IndexId", + "documentation":"

    the name of the index.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the IAM role that gives Amazon Kendra permission to write to your Amazon Cloudwatch logs.

    " + }, + "ServerSideEncryptionConfiguration":{ + "shape":"ServerSideEncryptionConfiguration", + "documentation":"

    The identifier of the AWS KMS customer master key (CMK) used to encrypt your data. Amazon Kendra doesn't support asymmetric CMKs.

    " + }, + "Status":{ + "shape":"IndexStatus", + "documentation":"

    The current status of the index. When the value is ACTIVE, the index is ready for use. If the Status field value is FAILED, the ErrorMessage field contains a message that explains why.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    The description of the index.

    " + }, + "CreatedAt":{ + "shape":"Timestamp", + "documentation":"

    The Unix datetime that the index was created.

    " + }, + "UpdatedAt":{ + "shape":"Timestamp", + "documentation":"

    The Unix datetime that the index was last updated.

    " + }, + "DocumentMetadataConfigurations":{ + "shape":"DocumentMetadataConfigurationList", + "documentation":"

    Configuration settings for any metadata applied to the documents in the index.

    " + }, + "IndexStatistics":{ + "shape":"IndexStatistics", + "documentation":"

    Provides information about the number of FAQ questions and answers and the number of text documents indexed.

    " + }, + "ErrorMessage":{ + "shape":"ErrorMessage", + "documentation":"

    When th eStatus field value is FAILED, the ErrorMessage field contains a message that explains why.

    " + } + } + }, + "Description":{ + "type":"string", + "max":1000, + "min":1, + "pattern":"^\\P{C}*$" + }, + "Document":{ + "type":"structure", + "required":["Id"], + "members":{ + "Id":{ + "shape":"DocumentId", + "documentation":"

    A unique identifier of the document in the index.

    " + }, + "Title":{ + "shape":"Title", + "documentation":"

    The title of the document.

    " + }, + "Blob":{ + "shape":"Blob", + "documentation":"

    The contents of the document.

    Documents passed to the Blob parameter must be base64 encoded. Your code might not need to encode the document file bytes if you're using an AWS SDK to call Amazon Kendra operations. If you are calling the Amazon Kendra endpoint directly using REST, you must base64 encode the contents before sending.

    " + }, + "S3Path":{"shape":"S3Path"}, + "Attributes":{ + "shape":"DocumentAttributeList", + "documentation":"

    Custom attributes to apply to the document. Use the custom attributes to provide additional information for searching, to provide facets for refining searches, and to provide additional information in the query response.

    " + }, + "AccessControlList":{ + "shape":"PrincipalList", + "documentation":"

    Information to use for user context filtering.

    " + }, + "ContentType":{ + "shape":"ContentType", + "documentation":"

    The file type of the document in the Blob field.

    " + } + }, + "documentation":"

    A document in an index.

    " + }, + "DocumentAttribute":{ + "type":"structure", + "required":[ + "Key", + "Value" + ], + "members":{ + "Key":{ + "shape":"DocumentAttributeKey", + "documentation":"

    The identifier for the attribute.

    " + }, + "Value":{ + "shape":"DocumentAttributeValue", + "documentation":"

    The value of the attribute.

    " + } + }, + "documentation":"

    A custom attribute value assigned to a document.

    " + }, + "DocumentAttributeKey":{ + "type":"string", + "max":200, + "min":1, + "pattern":"[a-zA-Z0-9_][a-zA-Z0-9_-]*" + }, + "DocumentAttributeKeyList":{ + "type":"list", + "member":{"shape":"DocumentAttributeKey"}, + "max":100, + "min":1 + }, + "DocumentAttributeList":{ + "type":"list", + "member":{"shape":"DocumentAttribute"}, + "max":100, + "min":1 + }, + "DocumentAttributeStringListValue":{ + "type":"list", + "member":{"shape":"String"}, + "max":5, + "min":1 + }, + "DocumentAttributeStringValue":{ + "type":"string", + "max":2048, + "min":1 + }, + "DocumentAttributeValue":{ + "type":"structure", + "members":{ + "StringValue":{ + "shape":"DocumentAttributeStringValue", + "documentation":"

    A string, such as \"department\".

    " + }, + "StringListValue":{ + "shape":"DocumentAttributeStringListValue", + "documentation":"

    A list of strings.

    " + }, + "LongValue":{ + "shape":"Long", + "documentation":"

    A long integer value.

    " + }, + "DateValue":{ + "shape":"Timestamp", + "documentation":"

    A date value expressed as seconds from the Unix epoch.

    " + } + }, + "documentation":"

    The value of a custom document attribute. You can only provide one value for a custom attribute.

    " + }, + "DocumentAttributeValueCountPair":{ + "type":"structure", + "members":{ + "DocumentAttributeValue":{ + "shape":"DocumentAttributeValue", + "documentation":"

    The value of the attribute. For example, \"HR.\"

    " + }, + "Count":{ + "shape":"Integer", + "documentation":"

    The number of documents in the response that have the attribute value for the key.

    " + } + }, + "documentation":"

    Provides the count of documents that match a particular attribute when doing a faceted search.

    " + }, + "DocumentAttributeValueCountPairList":{ + "type":"list", + "member":{"shape":"DocumentAttributeValueCountPair"} + }, + "DocumentAttributeValueType":{ + "type":"string", + "enum":[ + "STRING_VALUE", + "STRING_LIST_VALUE", + "LONG_VALUE", + "DATE_VALUE" + ] + }, + "DocumentId":{ + "type":"string", + "max":2048, + "min":1 + }, + "DocumentIdList":{ + "type":"list", + "member":{"shape":"DocumentId"}, + "max":10, + "min":1 + }, + "DocumentList":{ + "type":"list", + "member":{"shape":"Document"}, + "max":10, + "min":1 + }, + "DocumentMetadataBoolean":{"type":"boolean"}, + "DocumentMetadataConfiguration":{ + "type":"structure", + "required":[ + "Name", + "Type" + ], + "members":{ + "Name":{ + "shape":"DocumentMetadataConfigurationName", + "documentation":"

    The name of the index field.

    " + }, + "Type":{ + "shape":"DocumentAttributeValueType", + "documentation":"

    The data type of the index field.

    " + }, + "Relevance":{ + "shape":"Relevance", + "documentation":"

    Provides manual tuning parameters to determine how the field affects the search results.

    " + }, + "Search":{ + "shape":"Search", + "documentation":"

    Provides information about how the field is used during a search.

    " + } + }, + "documentation":"

    Specifies the properties of a custom index field.

    " + }, + "DocumentMetadataConfigurationList":{ + "type":"list", + "member":{"shape":"DocumentMetadataConfiguration"}, + "max":500, + "min":0 + }, + "DocumentMetadataConfigurationName":{ + "type":"string", + "max":30, + "min":1 + }, + "DocumentsMetadataConfiguration":{ + "type":"structure", + "members":{ + "S3Prefix":{ + "shape":"S3ObjectKey", + "documentation":"

    A prefix used to filter metadata configuration files in the AWS S3 bucket. The S3 bucket might contain multiple metadata files. Use S3Prefix to include only the desired metadata files.

    " + } + }, + "documentation":"

    Document metadata files that contain information such as the document access control information, source URI, document author, and custom attributes. Each metadata file contains metadata about a single document.

    " + }, + "Duration":{ + "type":"string", + "max":10, + "min":1, + "pattern":"[0-9]+[s]" + }, + "ErrorCode":{ + "type":"string", + "enum":[ + "InternalError", + "InvalidRequest" + ] + }, + "ErrorMessage":{ + "type":"string", + "max":2048, + "min":1, + "pattern":"^\\P{C}*$" + }, + "Facet":{ + "type":"structure", + "members":{ + "DocumentAttributeKey":{ + "shape":"DocumentAttributeKey", + "documentation":"

    The unique key for the document attribute.

    " + } + }, + "documentation":"

    Information a document attribute

    " + }, + "FacetList":{ + "type":"list", + "member":{"shape":"Facet"} + }, + "FacetResult":{ + "type":"structure", + "members":{ + "DocumentAttributeKey":{ + "shape":"DocumentAttributeKey", + "documentation":"

    The key for the facet values. This is the same as the DocumentAttributeKey provided in the query.

    " + }, + "DocumentAttributeValueCountPairs":{ + "shape":"DocumentAttributeValueCountPairList", + "documentation":"

    An array of key/value pairs, where the key is the value of the attribute and the count is the number of documents that share the key value.

    " + } + }, + "documentation":"

    The facet values for the documents in the response.

    " + }, + "FacetResultList":{ + "type":"list", + "member":{"shape":"FacetResult"} + }, + "FaqId":{ + "type":"string", + "max":100, + "min":1, + "pattern":"[a-zA-Z0-9][a-zA-Z0-9_-]*" + }, + "FaqName":{ + "type":"string", + "max":100, + "min":1, + "pattern":"[a-zA-Z0-9][a-zA-Z0-9_-]*" + }, + "FaqStatistics":{ + "type":"structure", + "required":["IndexedQuestionAnswersCount"], + "members":{ + "IndexedQuestionAnswersCount":{ + "shape":"IndexedQuestionAnswersCount", + "documentation":"

    The total number of FAQ questions and answers contained in the index.

    " + } + }, + "documentation":"

    Provides statistical information about the FAQ questions and answers contained in an index.

    " + }, + "FaqStatus":{ + "type":"string", + "enum":[ + "CREATING", + "UPDATING", + "ACTIVE", + "DELETING", + "FAILED" + ] + }, + "FaqSummary":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"FaqId", + "documentation":"

    The unique identifier of the FAQ.

    " + }, + "Name":{ + "shape":"FaqName", + "documentation":"

    The name that you assigned the FAQ when you created or updated the FAQ.

    " + }, + "Status":{ + "shape":"FaqStatus", + "documentation":"

    The current status of the FAQ. When the status is ACTIVE the FAQ is ready for use.

    " + }, + "CreatedAt":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime that the FAQ was added to the index.

    " + }, + "UpdatedAt":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime that the FAQ was last updated.

    " + } + }, + "documentation":"

    Provides information about a frequently asked questions and answer contained in an index.

    " + }, + "FaqSummaryItems":{ + "type":"list", + "member":{"shape":"FaqSummary"} + }, + "Highlight":{ + "type":"structure", + "required":[ + "BeginOffset", + "EndOffset" + ], + "members":{ + "BeginOffset":{ + "shape":"Integer", + "documentation":"

    The zero-based location in the response string where the highlight starts.

    " + }, + "EndOffset":{ + "shape":"Integer", + "documentation":"

    The zero-based location in the response string where the highlight ends.

    " + }, + "TopAnswer":{ + "shape":"Boolean", + "documentation":"

    Indicates whether the response is the best response. True if this is the best response; otherwise, false.

    " + } + }, + "documentation":"

    Provides information that you can use to highlight a search result so that your users can quickly identify terms in the response.

    " + }, + "HighlightList":{ + "type":"list", + "member":{"shape":"Highlight"} + }, + "Importance":{ + "type":"integer", + "max":10, + "min":1 + }, + "IndexConfigurationSummary":{ + "type":"structure", + "required":[ + "CreatedAt", + "UpdatedAt", + "Status" + ], + "members":{ + "Name":{ + "shape":"IndexName", + "documentation":"

    The name of the index.

    " + }, + "Id":{ + "shape":"IndexId", + "documentation":"

    A unique identifier for the index. Use this to identify the index when you are using operations such as Query, DescribeIndex, UpdateIndex, and DeleteIndex.

    " + }, + "CreatedAt":{ + "shape":"Timestamp", + "documentation":"

    The Unix timestamp when the index was created.

    " + }, + "UpdatedAt":{ + "shape":"Timestamp", + "documentation":"

    The Unix timestamp when the index was last updated by the UpdateIndex operation.

    " + }, + "Status":{ + "shape":"IndexStatus", + "documentation":"

    The current status of the index. When the status is ACTIVE, the index is ready to search.

    " + } + }, + "documentation":"

    A summary of information about an index.

    " + }, + "IndexConfigurationSummaryList":{ + "type":"list", + "member":{"shape":"IndexConfigurationSummary"} + }, + "IndexFieldName":{ + "type":"string", + "max":30, + "min":1, + "pattern":"^\\P{C}*$" + }, + "IndexId":{ + "type":"string", + "max":36, + "min":36, + "pattern":"[a-zA-Z0-9][a-zA-Z0-9-]*" + }, + "IndexName":{ + "type":"string", + "max":1000, + "min":1, + "pattern":"[a-zA-Z0-9][a-zA-Z0-9_-]*" + }, + "IndexStatistics":{ + "type":"structure", + "required":[ + "FaqStatistics", + "TextDocumentStatistics" + ], + "members":{ + "FaqStatistics":{ + "shape":"FaqStatistics", + "documentation":"

    The number of question and answer topics in the index.

    " + }, + "TextDocumentStatistics":{ + "shape":"TextDocumentStatistics", + "documentation":"

    The number of text documents indexed.

    " + } + }, + "documentation":"

    Provides information about the number of documents and the number of questions and answers in an index.

    " + }, + "IndexStatus":{ + "type":"string", + "enum":[ + "CREATING", + "ACTIVE", + "DELETING", + "FAILED", + "SYSTEM_UPDATING" + ] + }, + "IndexedQuestionAnswersCount":{ + "type":"integer", + "min":0 + }, + "IndexedTextDocumentsCount":{ + "type":"integer", + "min":0 + }, + "Integer":{"type":"integer"}, + "InternalServerException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true, + "fault":true + }, + "KmsKeyId":{ + "type":"string", + "max":2048, + "min":1, + "sensitive":true + }, + "ListDataSourceSyncJobsRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The identifier of the data source.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    If the result of the previous request to GetDataSourceSyncJobHistory was truncated, include the NextToken to fetch the next set of jobs.

    " + }, + "MaxResults":{ + "shape":"MaxResultsIntegerForListDataSourceSyncJobsRequest", + "documentation":"

    The maximum number of synchronization jobs to return in the response. If there are fewer results in the list, this response contains only the actual results.

    " + }, + "StartTimeFilter":{ + "shape":"TimeRange", + "documentation":"

    When specified, the synchronization jobs returned in the list are limited to jobs between the specified dates.

    " + }, + "StatusFilter":{ + "shape":"DataSourceSyncJobStatus", + "documentation":"

    When specified, only returns synchronization jobs with the Status field equal to the specified status.

    " + } + } + }, + "ListDataSourceSyncJobsResponse":{ + "type":"structure", + "members":{ + "History":{ + "shape":"DataSourceSyncJobHistoryList", + "documentation":"

    A history of synchronization jobs for the data source.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The GetDataSourceSyncJobHistory operation returns a page of vocabularies at a time. The maximum size of the page is set by the MaxResults parameter. If there are more jobs in the list than the page size, Amazon Kendra returns the NextPage token. Include the token in the next request to the GetDataSourceSyncJobHistory operation to return in the next page of jobs.

    " + } + } + }, + "ListDataSourcesRequest":{ + "type":"structure", + "required":["IndexId"], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    If the previous response was incomplete (because there is more data to retrieve), Amazon Kendra returns a pagination token in the response. You can use this pagination token to retrieve the next set of data sources (DataSourceSummaryItems).

    " + }, + "MaxResults":{ + "shape":"MaxResultsIntegerForListDataSourcesRequest", + "documentation":"

    The maximum number of data sources to return.

    " + } + } + }, + "ListDataSourcesResponse":{ + "type":"structure", + "members":{ + "SummaryItems":{ + "shape":"DataSourceSummaryList", + "documentation":"

    An array of summary information for one or more data sources.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    If the response is truncated, Amazon Kendra returns this token that you can use in the subsequent request to retrieve the next set of data sources.

    " + } + } + }, + "ListFaqsRequest":{ + "type":"structure", + "required":["IndexId"], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The index that contains the FAQ lists.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    If the result of the previous request to ListFaqs was truncated, include the NextToken to fetch the next set of FAQs.

    " + }, + "MaxResults":{ + "shape":"MaxResultsIntegerForListFaqsRequest", + "documentation":"

    The maximum number of FAQs to return in the response. If there are fewer results in the list, this response contains only the actual results.

    " + } + } + }, + "ListFaqsResponse":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The ListFaqs operation returns a page of FAQs at a time. The maximum size of the page is set by the MaxResults parameter. If there are more jobs in the list than the page size, Amazon Kendra returns the NextPage token. Include the token in the next request to the ListFaqs operation to return the next page of FAQs.

    " + }, + "FaqSummaryItems":{ + "shape":"FaqSummaryItems", + "documentation":"

    information about the FAQs associated with the specified index.

    " + } + } + }, + "ListIndicesRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    If the previous response was incomplete (because there is more data to retrieve), Amazon Kendra returns a pagination token in the response. You can use this pagination token to retrieve the next set of indexes (DataSourceSummaryItems).

    " + }, + "MaxResults":{ + "shape":"MaxResultsIntegerForListIndicesRequest", + "documentation":"

    The maximum number of data sources to return.

    " + } + } + }, + "ListIndicesResponse":{ + "type":"structure", + "members":{ + "IndexConfigurationSummaryItems":{ + "shape":"IndexConfigurationSummaryList", + "documentation":"

    An array of summary information for one or more indexes.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    If the response is truncated, Amazon Kendra returns this token that you can use in the subsequent request to retrieve the next set of indexes.

    " + } + } + }, + "Long":{"type":"long"}, + "MaxResultsIntegerForListDataSourceSyncJobsRequest":{ + "type":"integer", + "max":10, + "min":1 + }, + "MaxResultsIntegerForListDataSourcesRequest":{ + "type":"integer", + "max":100, + "min":1 + }, + "MaxResultsIntegerForListFaqsRequest":{ + "type":"integer", + "max":100, + "min":1 + }, + "MaxResultsIntegerForListIndicesRequest":{ + "type":"integer", + "max":100, + "min":1 + }, + "NextToken":{ + "type":"string", + "max":800, + "min":1 + }, + "Order":{ + "type":"string", + "enum":[ + "ASCENDING", + "DESCENDING" + ] + }, + "Principal":{ + "type":"structure", + "required":[ + "Name", + "Type", + "Access" + ], + "members":{ + "Name":{ + "shape":"PrincipalName", + "documentation":"

    The name of the user or group.

    " + }, + "Type":{ + "shape":"PrincipalType", + "documentation":"

    The type of principal.

    " + }, + "Access":{ + "shape":"ReadAccessType", + "documentation":"

    Whether to allow or deny access to the principal.

    " + } + }, + "documentation":"

    Provides user and group information for document access filtering.

    " + }, + "PrincipalList":{ + "type":"list", + "member":{"shape":"Principal"}, + "max":200, + "min":1 + }, + "PrincipalName":{ + "type":"string", + "max":200, + "min":1, + "pattern":"^\\P{C}*$" + }, + "PrincipalType":{ + "type":"string", + "enum":[ + "USER", + "GROUP" + ] + }, + "QueryId":{ + "type":"string", + "max":36, + "min":1 + }, + "QueryRequest":{ + "type":"structure", + "required":[ + "IndexId", + "QueryText" + ], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The unique identifier of the index to search. The identifier is returned in the response from the operation.

    " + }, + "QueryText":{ + "shape":"QueryText", + "documentation":"

    The text to search for.

    " + }, + "AttributeFilter":{ + "shape":"AttributeFilter", + "documentation":"

    Enables filtered searches based on document attributes. You can only provide one attribute filter; however, the AndAllFilters, NotFilter, and OrAllFilters parameters contain a list of other filters.

    The AttributeFilter parameter enables you to create a set of filtering rules that a document must satisfy to be included in the query results.

    " + }, + "Facets":{ + "shape":"FacetList", + "documentation":"

    An array of documents attributes. Amazon Kendra returns a count for each attribute key specified. You can use this information to help narrow the search for your user.

    " + }, + "RequestedDocumentAttributes":{ + "shape":"DocumentAttributeKeyList", + "documentation":"

    An array of document attributes to include in the response. No other document attributes are included in the response. By default all document attributes are included in the response.

    " + }, + "QueryResultTypeFilter":{ + "shape":"QueryResultType", + "documentation":"

    Sets the type of query. Only results for the specified query type are returned.

    " + }, + "PageNumber":{ + "shape":"Integer", + "documentation":"

    Query results are returned in pages the size of the PageSize parameter. By default, Amazon Kendra returns the first page of results. Use this parameter to get result pages after the first one.

    " + }, + "PageSize":{ + "shape":"Integer", + "documentation":"

    Sets the number of results that are returned in each page of results. The default page size is 100.

    " + } + } + }, + "QueryResult":{ + "type":"structure", + "members":{ + "QueryId":{ + "shape":"QueryId", + "documentation":"

    The unique identifier for the search. You use QueryId to identify the search when using the feedback API.

    " + }, + "ResultItems":{ + "shape":"QueryResultItemList", + "documentation":"

    The results of the search.

    " + }, + "FacetResults":{ + "shape":"FacetResultList", + "documentation":"

    Contains the facet results. A FacetResult contains the counts for each attribute key that was specified in the Facets input parameter.

    " + }, + "TotalNumberOfResults":{ + "shape":"Integer", + "documentation":"

    The number of items returned by the search. Use this to determine when you have requested the last set of results.

    " + } + } + }, + "QueryResultItem":{ + "type":"structure", + "members":{ + "Id":{ + "shape":"ResultId", + "documentation":"

    The unique identifier for the query result.

    " + }, + "Type":{ + "shape":"QueryResultType", + "documentation":"

    The type of document.

    " + }, + "AdditionalAttributes":{ + "shape":"AdditionalResultAttributeList", + "documentation":"

    " + }, + "DocumentId":{ + "shape":"DocumentId", + "documentation":"

    The unique identifier for the document.

    " + }, + "DocumentTitle":{ + "shape":"TextWithHighlights", + "documentation":"

    The title of the document. Contains the text of the title and information for highlighting the relevant terms in the title.

    " + }, + "DocumentExcerpt":{ + "shape":"TextWithHighlights", + "documentation":"

    An extract of the text in the document. Contains information about highlighting the relevant terms in the excerpt.

    " + }, + "DocumentURI":{ + "shape":"Url", + "documentation":"

    The URI of the original location of the document.

    " + }, + "DocumentAttributes":{ + "shape":"DocumentAttributeList", + "documentation":"

    An array of document attributes for the document that the query result maps to. For example, the document author (Author) or the source URI (SourceUri) of the document.

    " + } + }, + "documentation":"

    A single query result.

    A query result contains information about a document returned by the query. This includes the original location of the document, a list of attributes assigned to the document, and relevant text from the document that satisfies the query.

    " + }, + "QueryResultItemList":{ + "type":"list", + "member":{"shape":"QueryResultItem"} + }, + "QueryResultType":{ + "type":"string", + "enum":[ + "DOCUMENT", + "QUESTION_ANSWER", + "ANSWER" + ] + }, + "QueryText":{ + "type":"string", + "max":1000, + "min":1, + "pattern":"^\\P{C}*$" + }, + "ReadAccessType":{ + "type":"string", + "enum":[ + "ALLOW", + "DENY" + ] + }, + "Relevance":{ + "type":"structure", + "members":{ + "Freshness":{ + "shape":"DocumentMetadataBoolean", + "documentation":"

    Indicates that this field determines how \"fresh\" a document is. For example, if document 1 was created on November 5, and document 2 was created on October 31, document 1 is \"fresher\" than document 2. You can only set the Freshness field on one DATE type field. Only applies to DATE fields.

    " + }, + "Importance":{ + "shape":"Importance", + "documentation":"

    The relative importance of the field in the search. Larger numbers provide more of a boost than smaller numbers.

    " + }, + "Duration":{ + "shape":"Duration", + "documentation":"

    Specifies the time period that the boost applies to. For example, to make the boost apply to documents with the field value within the last month, you would use \"2628000s\". Once the field value is beyond the specified range, the effect of the boost drops off. The higher the importance, the faster the effect drops off. If you don't specify a value, the default is 3 months. The value of the field is a numeric string followed by the character \"s\", for example \"86400s\" for one day, or \"604800s\" for one week.

    Only applies to DATE fields.

    " + }, + "RankOrder":{ + "shape":"Order", + "documentation":"

    Determines how values should be interpreted.

    When the RankOrder field is ASCENDING, higher numbers are better. For example, a document with a rating score of 10 is higher ranking than a document with a rating score of 1.

    When the RankOrder field is DESCENDING, lower numbers are better. For example, in a task tracking application, a priority 1 task is more important than a priority 5 task.

    Only applies to LONG and DOUBLE fields.

    " + }, + "ValueImportanceMap":{ + "shape":"ValueImportanceMap", + "documentation":"

    A list of values that should be given a different boost when they appear in the result list. For example, if you are boosting a field called \"department,\" query terms that match the department field are boosted in the result. However, you can add entries from the department field to boost documents with those values higher.

    For example, you can add entries to the map with names of departments. If you add \"HR\",5 and \"Legal\",3 those departments are given special attention when they appear in the metadata of a document. When those terms appear they are given the specified importance instead of the regular importance for the boost.

    " + } + }, + "documentation":"

    Provides information for manually tuning the relevance of a field in a search. When a query includes terms that match the field, the results are given a boost in the response based on these tuning parameters.

    " + }, + "RelevanceFeedback":{ + "type":"structure", + "required":[ + "ResultId", + "RelevanceValue" + ], + "members":{ + "ResultId":{ + "shape":"ResultId", + "documentation":"

    The unique identifier of the search result that the user provided relevance feedback for.

    " + }, + "RelevanceValue":{ + "shape":"RelevanceType", + "documentation":"

    Whether to document was relevant or not relevant to the search.

    " + } + }, + "documentation":"

    Provides feedback on how relevant a document is to a search. Your application uses the SubmitFeedback operation to provide relevance information.

    " + }, + "RelevanceFeedbackList":{ + "type":"list", + "member":{"shape":"RelevanceFeedback"} + }, + "RelevanceType":{ + "type":"string", + "enum":[ + "RELEVANT", + "NOT_RELEVANT" + ] + }, + "ResourceAlreadyExistException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "ResourceInUseException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "ResourceUnavailableException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "ResultId":{ + "type":"string", + "max":73, + "min":1 + }, + "RoleArn":{ + "type":"string", + "max":1284, + "min":1, + "pattern":"arn:[a-z0-9-\\.]{1,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023}" + }, + "S3BucketName":{ + "type":"string", + "max":63, + "min":3, + "pattern":"[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9]" + }, + "S3DataSourceConfiguration":{ + "type":"structure", + "required":["BucketName"], + "members":{ + "BucketName":{ + "shape":"S3BucketName", + "documentation":"

    The name of the bucket that contains the documents.

    " + }, + "InclusionPrefixes":{ + "shape":"DataSourceInclusionsExclusionsStrings", + "documentation":"

    A list of S3 prefixes for the documents that should be included in the index.

    " + }, + "ExclusionPatterns":{ + "shape":"DataSourceInclusionsExclusionsStrings", + "documentation":"

    A list of glob patterns for documents that should not be indexed. If a document that matches an inclusion prefix also matches an exclusion pattern, the document is not indexed.

    For more information about glob patterns, see glob (programming) in Wikipedia.

    " + }, + "DocumentsMetadataConfiguration":{"shape":"DocumentsMetadataConfiguration"}, + "AccessControlListConfiguration":{ + "shape":"AccessControlListConfiguration", + "documentation":"

    Provides the path to the S3 bucket that contains the user context filtering files for the data source.

    " + } + }, + "documentation":"

    Provides configuration information for a data source to index documents in an Amazon S3 bucket.

    " + }, + "S3ObjectKey":{ + "type":"string", + "max":1024, + "min":1 + }, + "S3Path":{ + "type":"structure", + "required":[ + "Bucket", + "Key" + ], + "members":{ + "Bucket":{ + "shape":"S3BucketName", + "documentation":"

    The name of the S3 bucket that contains the file.

    " + }, + "Key":{ + "shape":"S3ObjectKey", + "documentation":"

    The name of the file.

    " + } + }, + "documentation":"

    Information required to find a specific file in an Amazon S3 bucket.

    " + }, + "ScanSchedule":{"type":"string"}, + "Search":{ + "type":"structure", + "members":{ + "Facetable":{ + "shape":"Boolean", + "documentation":"

    Indicates that the field can be used to create search facets, a count of results for each value in the field. The default is false .

    " + }, + "Searchable":{ + "shape":"Boolean", + "documentation":"

    Determines whether the field is used in the search. If the Searchable field is true, you can use relevance tuning to manually tune how Amazon Kendra weights the field in the search. The default is true for string fields and false for number and date fields.

    " + }, + "Displayable":{ + "shape":"Boolean", + "documentation":"

    Determines whether the field is returned in the query response. The default is true.

    " + } + }, + "documentation":"

    Provides information about how a custom index field is used during a search.

    " + }, + "SecretArn":{ + "type":"string", + "max":1284, + "min":1, + "pattern":"arn:[a-z0-9-\\.]{1,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[a-z0-9-\\.]{0,63}:[^/].{0,1023}" + }, + "SecurityGroupIdList":{ + "type":"list", + "member":{"shape":"VpcSecurityGroupId"}, + "max":10, + "min":1 + }, + "ServerSideEncryptionConfiguration":{ + "type":"structure", + "members":{ + "KmsKeyId":{ + "shape":"KmsKeyId", + "documentation":"

    The identifier of the AWS KMS customer master key (CMK). Amazon Kendra doesn't support asymmetric CMKs.

    " + } + }, + "documentation":"

    Provides the identifier of the AWS KMS customer master key (CMK) used to encrypt data indexed by Amazon Kendra. Amazon Kendra doesn't support asymmetric CMKs.

    " + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "SharePointConfiguration":{ + "type":"structure", + "required":[ + "SharePointVersion", + "Urls", + "SecretArn" + ], + "members":{ + "SharePointVersion":{ + "shape":"SharePointVersion", + "documentation":"

    The version of Microsoft SharePoint that you are using as a data source.

    " + }, + "Urls":{ + "shape":"SharePointUrlList", + "documentation":"

    The URLs of the Microsoft SharePoint site that contains the documents that should be indexed.

    " + }, + "SecretArn":{ + "shape":"SecretArn", + "documentation":"

    The Amazon Resource Name (ARN) of credentials stored in AWS Secrets Manager. The credentials should be a user/password pair. For more information, see Using a Microsoft SharePoint Data Source. For more information about AWS Secrets Manager, see What Is AWS Secrets Manager in the AWS Secrets Manager user guide.

    " + }, + "CrawlAttachments":{ + "shape":"Boolean", + "documentation":"

    TRUE to include attachments to documents stored in your Microsoft SharePoint site in the index; otherwise, FALSE.

    " + }, + "UseChangeLog":{ + "shape":"Boolean", + "documentation":"

    Set to TRUE to use the Microsoft SharePoint change log to determine the documents that need to be updated in the index. Depending on the size of the SharePoint change log, it may take longer for Amazon Kendra to use the change log than it takes it to determine the changed documents using the Amazon Kendra document crawler.

    " + }, + "InclusionPatterns":{ + "shape":"DataSourceInclusionsExclusionsStrings", + "documentation":"

    A list of regular expression patterns. Documents that match the patterns are included in the index. Documents that don't match the patterns are excluded from the index. If a document matches both an inclusion pattern and an exclusion pattern, the document is not included in the index.

    The regex is applied to the display URL of the SharePoint document.

    " + }, + "ExclusionPatterns":{ + "shape":"DataSourceInclusionsExclusionsStrings", + "documentation":"

    A list of regular expression patterns. Documents that match the patterns are excluded from the index. Documents that don't match the patterns are included in the index. If a document matches both an exclusion pattern and an inclusion pattern, the document is not included in the index.

    The regex is applied to the display URL of the SharePoint document.

    " + }, + "VpcConfiguration":{"shape":"DataSourceVpcConfiguration"}, + "FieldMappings":{ + "shape":"DataSourceToIndexFieldMappingList", + "documentation":"

    A list of DataSourceToIndexFieldMapping objects that map Microsoft SharePoint attributes to custom fields in the Amazon Kendra index. You must first create the index fields using the operation before you map SharePoint attributes. For more information, see Mapping Data Source Fields.

    " + }, + "DocumentTitleFieldName":{ + "shape":"DataSourceFieldName", + "documentation":"

    The Microsoft SharePoint attribute field that contains the title of the document.

    " + } + }, + "documentation":"

    Provides configuration information for connecting to a Microsoft SharePoint data source.

    " + }, + "SharePointUrlList":{ + "type":"list", + "member":{"shape":"Url"}, + "max":100, + "min":1 + }, + "SharePointVersion":{ + "type":"string", + "enum":["SHAREPOINT_ONLINE"] + }, + "StartDataSourceSyncJobRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The identifier of the data source to synchronize.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source.

    " + } + } + }, + "StartDataSourceSyncJobResponse":{ + "type":"structure", + "members":{ + "ExecutionId":{ + "shape":"String", + "documentation":"

    Identifies a particular synchronization job.

    " + } + } + }, + "StopDataSourceSyncJobRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The identifier of the data source for which to stop the synchronization jobs.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source.

    " + } + } + }, + "String":{ + "type":"string", + "max":2048, + "min":1 + }, + "SubmitFeedbackRequest":{ + "type":"structure", + "required":[ + "IndexId", + "QueryId" + ], + "members":{ + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that was queried.

    " + }, + "QueryId":{ + "shape":"QueryId", + "documentation":"

    The identifier of the specific query for which you are submitting feedback. The query ID is returned in the response to the operation.

    " + }, + "ClickFeedbackItems":{ + "shape":"ClickFeedbackList", + "documentation":"

    Tells Amazon Kendra that a particular search result link was chosen by the user.

    " + }, + "RelevanceFeedbackItems":{ + "shape":"RelevanceFeedbackList", + "documentation":"

    Provides Amazon Kendra with relevant or not relevant feedback for whether a particular item was relevant to the search.

    " + } + } + }, + "SubnetId":{ + "type":"string", + "max":200, + "min":1, + "pattern":"[\\-0-9a-zA-Z]+" + }, + "SubnetIdList":{ + "type":"list", + "member":{"shape":"SubnetId"}, + "max":6, + "min":1 + }, + "TableName":{ + "type":"string", + "max":100, + "min":1, + "pattern":"^[a-zA-Z][a-zA-Z0-9_]*$" + }, + "TextDocumentStatistics":{ + "type":"structure", + "required":["IndexedTextDocumentsCount"], + "members":{ + "IndexedTextDocumentsCount":{ + "shape":"IndexedTextDocumentsCount", + "documentation":"

    The number of text documents indexed.

    " + } + }, + "documentation":"

    Provides information about text documents indexed in an index.

    " + }, + "TextWithHighlights":{ + "type":"structure", + "members":{ + "Text":{ + "shape":"String", + "documentation":"

    The text to display to the user.

    " + }, + "Highlights":{ + "shape":"HighlightList", + "documentation":"

    The beginning and end of the text that should be highlighted.

    " + } + }, + "documentation":"

    Provides text and information about where to highlight the text.

    " + }, + "ThrottlingException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "TimeRange":{ + "type":"structure", + "members":{ + "StartTime":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime of the beginning of the time range.

    " + }, + "EndTime":{ + "shape":"Timestamp", + "documentation":"

    The UNIX datetime of the end of the time range.

    " + } + }, + "documentation":"

    Provides a range of time.

    " + }, + "Timestamp":{"type":"timestamp"}, + "Title":{ + "type":"string", + "max":1024, + "min":1 + }, + "UpdateDataSourceRequest":{ + "type":"structure", + "required":[ + "Id", + "IndexId" + ], + "members":{ + "Id":{ + "shape":"DataSourceId", + "documentation":"

    The unique identifier of the data source to update.

    " + }, + "Name":{ + "shape":"DataSourceName", + "documentation":"

    The name of the data source to update. The name of the data source can't be updated. To rename a data source you must delete the data source and re-create it.

    " + }, + "IndexId":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index that contains the data source to update.

    " + }, + "Configuration":{"shape":"DataSourceConfiguration"}, + "Description":{ + "shape":"Description", + "documentation":"

    The new description for the data source.

    " + }, + "Schedule":{ + "shape":"ScanSchedule", + "documentation":"

    The new update schedule for the data source.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    The Amazon Resource Name (ARN) of the new role to use when the data source is accessing resources on your behalf.

    " + } + } + }, + "UpdateIndexRequest":{ + "type":"structure", + "required":["Id"], + "members":{ + "Id":{ + "shape":"IndexId", + "documentation":"

    The identifier of the index to update.

    " + }, + "Name":{ + "shape":"IndexName", + "documentation":"

    The name of the index to update.

    " + }, + "RoleArn":{ + "shape":"RoleArn", + "documentation":"

    A new IAM role that gives Amazon Kendra permission to access your Amazon CloudWatch logs.

    " + }, + "Description":{ + "shape":"Description", + "documentation":"

    A new description for the index.

    " + }, + "DocumentMetadataConfigurationUpdates":{ + "shape":"DocumentMetadataConfigurationList", + "documentation":"

    The document metadata to update.

    " + } + } + }, + "Url":{ + "type":"string", + "max":2048, + "min":1, + "pattern":"^(https?|ftp|file):\\/\\/(.*)" + }, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "ValueImportanceMap":{ + "type":"map", + "key":{"shape":"ValueImportanceMapKey"}, + "value":{"shape":"Importance"} + }, + "ValueImportanceMapKey":{ + "type":"string", + "max":50, + "min":1 + }, + "VpcSecurityGroupId":{ + "type":"string", + "max":200, + "min":1, + "pattern":"[-0-9a-zA-Z]+" + } + }, + "documentation":"

    Amazon Kendra is a service for indexing large document sets.

    " +} diff --git a/services/kinesis/build.properties b/services/kinesis/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/kinesis/build.properties +++ b/services/kinesis/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/kinesis/pom.xml b/services/kinesis/pom.xml index 99ad9ededa8e..425b533b1a49 100644 --- a/services/kinesis/pom.xml +++ b/services/kinesis/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + kinesisvideosignaling + AWS Java SDK :: Services :: Kinesis Video Signaling + The AWS Java SDK for Kinesis Video Signaling module holds the client classes that are used for + communicating with Kinesis Video Signaling. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.kinesisvideosignaling + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/kinesisvideosignaling/src/main/resources/codegen-resources/paginators-1.json b/services/kinesisvideosignaling/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..5677bd8e4a2d --- /dev/null +++ b/services/kinesisvideosignaling/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,4 @@ +{ + "pagination": { + } +} diff --git a/services/kinesisvideosignaling/src/main/resources/codegen-resources/service-2.json b/services/kinesisvideosignaling/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..9f6f60e216f1 --- /dev/null +++ b/services/kinesisvideosignaling/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,249 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-12-04", + "endpointPrefix":"kinesisvideo", + "protocol":"rest-json", + "serviceAbbreviation":"Amazon Kinesis Video Signaling Channels", + "serviceFullName":"Amazon Kinesis Video Signaling Channels", + "serviceId":"Kinesis Video Signaling", + "signatureVersion":"v4", + "uid":"kinesis-video-signaling-2019-12-04" + }, + "operations":{ + "GetIceServerConfig":{ + "name":"GetIceServerConfig", + "http":{ + "method":"POST", + "requestUri":"/v1/get-ice-server-config" + }, + "input":{"shape":"GetIceServerConfigRequest"}, + "output":{"shape":"GetIceServerConfigResponse"}, + "errors":[ + {"shape":"InvalidClientException"}, + {"shape":"SessionExpiredException"}, + {"shape":"ClientLimitExceededException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InvalidArgumentException"}, + {"shape":"NotAuthorizedException"} + ], + "documentation":"

    Gets the Interactive Connectivity Establishment (ICE) server configuration information, including URIs, username, and password which can be used to configure the WebRTC connection. The ICE component uses this configuration information to setup the WebRTC connection, including authenticating with the Traversal Using Relays around NAT (TURN) relay server.

    TURN is a protocol that is used to improve the connectivity of peer-to-peer applications. By providing a cloud-based relay service, TURN ensures that a connection can be established even when one or more peers are incapable of a direct peer-to-peer connection. For more information, see A REST API For Access To TURN Services.

    You can invoke this API to establish a fallback mechanism in case either of the peers is unable to establish a direct peer-to-peer connection over a signaling channel. You must specify either a signaling channel ARN or the client ID in order to invoke this API.

    " + }, + "SendAlexaOfferToMaster":{ + "name":"SendAlexaOfferToMaster", + "http":{ + "method":"POST", + "requestUri":"/v1/send-alexa-offer-to-master" + }, + "input":{"shape":"SendAlexaOfferToMasterRequest"}, + "output":{"shape":"SendAlexaOfferToMasterResponse"}, + "errors":[ + {"shape":"ClientLimitExceededException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"InvalidArgumentException"}, + {"shape":"NotAuthorizedException"} + ], + "documentation":"

    This API allows you to connect WebRTC-enabled devices with Alexa display devices. When invoked, it sends the Alexa Session Description Protocol (SDP) offer to the master peer. The offer is delivered as soon as the master is connected to the specified signaling channel. This API returns the SDP answer from the connected master. If the master is not connected to the signaling channel, redelivery requests are made until the message expires.

    " + } + }, + "shapes":{ + "Answer":{ + "type":"string", + "max":10000, + "min":1 + }, + "ClientId":{ + "type":"string", + "max":256, + "min":1, + "pattern":"[a-zA-Z0-9_.-]+" + }, + "ClientLimitExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Your request was throttled because you have exceeded the limit of allowed client calls. Try making the call later.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ErrorMessage":{"type":"string"}, + "GetIceServerConfigRequest":{ + "type":"structure", + "required":["ChannelARN"], + "members":{ + "ChannelARN":{ + "shape":"ResourceARN", + "documentation":"

    The ARN of the signaling channel to be used for the peer-to-peer connection between configured peers.

    " + }, + "ClientId":{ + "shape":"ClientId", + "documentation":"

    Unique identifier for the viewer. Must be unique within the signaling channel.

    " + }, + "Service":{ + "shape":"Service", + "documentation":"

    Specifies the desired service. Currently, TURN is the only valid value.

    " + }, + "Username":{ + "shape":"Username", + "documentation":"

    An optional user ID to be associated with the credentials.

    " + } + } + }, + "GetIceServerConfigResponse":{ + "type":"structure", + "members":{ + "IceServerList":{ + "shape":"IceServerList", + "documentation":"

    The list of ICE server information objects.

    " + } + } + }, + "IceServer":{ + "type":"structure", + "members":{ + "Uris":{ + "shape":"Uris", + "documentation":"

    An array of URIs, in the form specified in the I-D.petithuguenin-behave-turn-uris spec. These URIs provide the different addresses and/or protocols that can be used to reach the TURN server.

    " + }, + "Username":{ + "shape":"Username", + "documentation":"

    A username to login to the ICE server.

    " + }, + "Password":{ + "shape":"Password", + "documentation":"

    A password to login to the ICE server.

    " + }, + "Ttl":{ + "shape":"Ttl", + "documentation":"

    The period of time, in seconds, during which the username and password are valid.

    " + } + }, + "documentation":"

    A structure for the ICE server connection data.

    " + }, + "IceServerList":{ + "type":"list", + "member":{"shape":"IceServer"} + }, + "InvalidArgumentException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The value for this input parameter is invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidClientException":{ + "type":"structure", + "members":{ + "message":{"shape":"errorMessage"} + }, + "documentation":"

    The specified client is invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "MessagePayload":{ + "type":"string", + "max":10000, + "min":1, + "pattern":"[a-zA-Z0-9+/=]+" + }, + "NotAuthorizedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The caller is not authorized to perform this operation.

    ", + "error":{"httpStatusCode":401}, + "exception":true + }, + "Password":{ + "type":"string", + "max":256, + "min":1, + "pattern":"[a-zA-Z0-9_.-]+" + }, + "ResourceARN":{ + "type":"string", + "max":1024, + "min":1, + "pattern":"arn:aws:kinesisvideo:[a-z0-9-]+:[0-9]+:[a-z]+/[a-zA-Z0-9_.-]+/[0-9]+" + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The specified resource is not found.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "SendAlexaOfferToMasterRequest":{ + "type":"structure", + "required":[ + "ChannelARN", + "SenderClientId", + "MessagePayload" + ], + "members":{ + "ChannelARN":{ + "shape":"ResourceARN", + "documentation":"

    The ARN of the signaling channel by which Alexa and the master peer communicate.

    " + }, + "SenderClientId":{ + "shape":"ClientId", + "documentation":"

    The unique identifier for the sender client.

    " + }, + "MessagePayload":{ + "shape":"MessagePayload", + "documentation":"

    The base64-encoded SDP offer content.

    " + } + } + }, + "SendAlexaOfferToMasterResponse":{ + "type":"structure", + "members":{ + "Answer":{ + "shape":"Answer", + "documentation":"

    The base64-encoded SDP answer content.

    " + } + } + }, + "Service":{ + "type":"string", + "enum":["TURN"] + }, + "SessionExpiredException":{ + "type":"structure", + "members":{ + "message":{"shape":"errorMessage"} + }, + "documentation":"

    If the client session is expired. Once the client is connected, the session is valid for 45 minutes. Client should reconnect to the channel to continue sending/receiving messages.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "Ttl":{ + "type":"integer", + "max":86400, + "min":30 + }, + "Uri":{ + "type":"string", + "max":256, + "min":1 + }, + "Uris":{ + "type":"list", + "member":{"shape":"Uri"} + }, + "Username":{ + "type":"string", + "max":256, + "min":1, + "pattern":"[a-zA-Z0-9_.-]+" + }, + "errorMessage":{"type":"string"} + }, + "documentation":"

    Kinesis Video Streams Signaling Service is a intermediate service that establishes a communication channel for discovering peers, transmitting offers and answers in order to establish peer-to-peer connection in webRTC technology.

    " +} diff --git a/services/kms/build.properties b/services/kms/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/kms/build.properties +++ b/services/kms/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/kms/pom.xml b/services/kms/pom.xml index 7e8c1b62b9e5..9114e045a9e7 100644 --- a/services/kms/pom.xml +++ b/services/kms/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + marketplacecatalog + AWS Java SDK :: Services :: Marketplace Catalog + The AWS Java SDK for Marketplace Catalog module holds the client classes that are used for + communicating with Marketplace Catalog. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.marketplacecatalog + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/marketplacecatalog/src/main/resources/codegen-resources/paginators-1.json b/services/marketplacecatalog/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..dbcad8968038 --- /dev/null +++ b/services/marketplacecatalog/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,14 @@ +{ + "pagination": { + "ListChangeSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListEntities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/marketplacecatalog/src/main/resources/codegen-resources/service-2.json b/services/marketplacecatalog/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..1a01518c04ae --- /dev/null +++ b/services/marketplacecatalog/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,768 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2018-09-17", + "endpointPrefix":"catalog.marketplace", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"AWS Marketplace Catalog", + "serviceFullName":"AWS Marketplace Catalog Service", + "serviceId":"Marketplace Catalog", + "signatureVersion":"v4", + "signingName":"aws-marketplace", + "uid":"marketplace-catalog-2018-09-17" + }, + "operations":{ + "CancelChangeSet":{ + "name":"CancelChangeSet", + "http":{ + "method":"PATCH", + "requestUri":"/CancelChangeSet" + }, + "input":{"shape":"CancelChangeSetRequest"}, + "output":{"shape":"CancelChangeSetResponse"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Used to cancel an open change request. Must be sent before the status of the request changes to APPLYING, the final stage of completing your change request. You can describe a change during the 60-day request history retention period for API calls.

    " + }, + "DescribeChangeSet":{ + "name":"DescribeChangeSet", + "http":{ + "method":"GET", + "requestUri":"/DescribeChangeSet" + }, + "input":{"shape":"DescribeChangeSetRequest"}, + "output":{"shape":"DescribeChangeSetResponse"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Provides information about a given change set.

    " + }, + "DescribeEntity":{ + "name":"DescribeEntity", + "http":{ + "method":"GET", + "requestUri":"/DescribeEntity" + }, + "input":{"shape":"DescribeEntityRequest"}, + "output":{"shape":"DescribeEntityResponse"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ValidationException"}, + {"shape":"ResourceNotSupportedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns the metadata and content of the entity.

    " + }, + "ListChangeSets":{ + "name":"ListChangeSets", + "http":{ + "method":"POST", + "requestUri":"/ListChangeSets" + }, + "input":{"shape":"ListChangeSetsRequest"}, + "output":{"shape":"ListChangeSetsResponse"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Returns the list of change sets owned by the account being used to make the call. You can filter this list by providing any combination of entityId, ChangeSetName, and status. If you provide more than one filter, the API operation applies a logical AND between the filters.

    You can describe a change during the 60-day request history retention period for API calls.

    " + }, + "ListEntities":{ + "name":"ListEntities", + "http":{ + "method":"POST", + "requestUri":"/ListEntities" + }, + "input":{"shape":"ListEntitiesRequest"}, + "output":{"shape":"ListEntitiesResponse"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"} + ], + "documentation":"

    Provides the list of entities of a given type.

    " + }, + "StartChangeSet":{ + "name":"StartChangeSet", + "http":{ + "method":"POST", + "requestUri":"/StartChangeSet" + }, + "input":{"shape":"StartChangeSetRequest"}, + "output":{"shape":"StartChangeSetResponse"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ResourceInUseException"}, + {"shape":"ThrottlingException"}, + {"shape":"ServiceQuotaExceededException"} + ], + "documentation":"

    This operation allows you to request changes in your entities.

    " + } + }, + "shapes":{ + "ARN":{ + "type":"string", + "max":2048, + "min":1, + "pattern":"^[a-zA-Z0-9:*/-]+$" + }, + "AccessDeniedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    Access is denied.

    ", + "error":{"httpStatusCode":403}, + "exception":true, + "synthetic":true + }, + "CancelChangeSetRequest":{ + "type":"structure", + "required":[ + "Catalog", + "ChangeSetId" + ], + "members":{ + "Catalog":{ + "shape":"Catalog", + "documentation":"

    Required. The catalog related to the request. Fixed value: AWSMarketplace.

    ", + "location":"querystring", + "locationName":"catalog" + }, + "ChangeSetId":{ + "shape":"ResourceId", + "documentation":"

    Required. The unique identifier of the StartChangeSet request that you want to cancel.

    ", + "location":"querystring", + "locationName":"changeSetId" + } + } + }, + "CancelChangeSetResponse":{ + "type":"structure", + "members":{ + "ChangeSetId":{ + "shape":"ResourceId", + "documentation":"

    The unique identifier for the change set referenced in this request.

    " + }, + "ChangeSetArn":{ + "shape":"ARN", + "documentation":"

    The ARN associated with the change set referenced in this request.

    " + } + } + }, + "Catalog":{ + "type":"string", + "max":64, + "min":1, + "pattern":"^[a-zA-Z]+$" + }, + "Change":{ + "type":"structure", + "required":[ + "ChangeType", + "Entity", + "Details" + ], + "members":{ + "ChangeType":{ + "shape":"ChangeType", + "documentation":"

    Change types are single string values that describe your intention for the change. Each change type is unique for each EntityType provided in the change's scope.

    " + }, + "Entity":{ + "shape":"Entity", + "documentation":"

    The entity to be changed.

    " + }, + "Details":{ + "shape":"Json", + "documentation":"

    This object contains details specific to the change type of the requested change.

    " + } + }, + "documentation":"

    An object that contains the ChangeType, Details, and Entity.

    " + }, + "ChangeSetDescription":{ + "type":"list", + "member":{"shape":"ChangeSummary"} + }, + "ChangeSetName":{ + "type":"string", + "max":100, + "min":1, + "pattern":"^[\\w\\s+=.:@-]+$" + }, + "ChangeSetSummaryList":{ + "type":"list", + "member":{"shape":"ChangeSetSummaryListItem"} + }, + "ChangeSetSummaryListItem":{ + "type":"structure", + "members":{ + "ChangeSetId":{ + "shape":"ResourceId", + "documentation":"

    The unique identifier for a change set.

    " + }, + "ChangeSetArn":{ + "shape":"ARN", + "documentation":"

    The ARN associated with the unique identifier for the change set referenced in this request.

    " + }, + "ChangeSetName":{ + "shape":"ChangeSetName", + "documentation":"

    The non-unique name for the change set.

    " + }, + "StartTime":{ + "shape":"DateTimeISO8601", + "documentation":"

    The time, in ISO 8601 format (2018-02-27T13:45:22Z), when the change set was started.

    " + }, + "EndTime":{ + "shape":"DateTimeISO8601", + "documentation":"

    The time, in ISO 8601 format (2018-02-27T13:45:22Z), when the change set was finished.

    " + }, + "Status":{ + "shape":"ChangeStatus", + "documentation":"

    The current status of the change set.

    " + }, + "EntityIdList":{ + "shape":"ResourceIdList", + "documentation":"

    This object is a list of entity IDs (string) that are a part of a change set. The entity ID list is a maximum of 20 entities. It must contain at least one entity.

    " + } + }, + "documentation":"

    A summary of a change set returned in a list of change sets when the ListChangeSets action is called.

    " + }, + "ChangeStatus":{ + "type":"string", + "enum":[ + "PREPARING", + "APPLYING", + "SUCCEEDED", + "CANCELLED", + "FAILED" + ] + }, + "ChangeSummary":{ + "type":"structure", + "members":{ + "ChangeType":{ + "shape":"ChangeType", + "documentation":"

    The type of the change.

    " + }, + "Entity":{ + "shape":"Entity", + "documentation":"

    The entity to be changed.

    " + }, + "ErrorDetailList":{ + "shape":"ErrorDetailList", + "documentation":"

    An array of ErrorDetail objects associated with the change.

    " + } + }, + "documentation":"

    This object is a container for common summary information about the change. The summary doesn't contain the whole change structure.

    " + }, + "ChangeType":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[A-Z][\\w]*$" + }, + "ClientRequestToken":{ + "type":"string", + "max":36, + "min":1, + "pattern":"^[\\w\\-]+$" + }, + "DateTimeISO8601":{ + "type":"string", + "max":20, + "min":20, + "pattern":"^([\\d]{4})\\-(1[0-2]|0[1-9])\\-(3[01]|0[1-9]|[12][\\d])T(2[0-3]|[01][\\d]):([0-5][\\d]):([0-5][\\d])Z$" + }, + "DescribeChangeSetRequest":{ + "type":"structure", + "required":[ + "Catalog", + "ChangeSetId" + ], + "members":{ + "Catalog":{ + "shape":"Catalog", + "documentation":"

    Required. The catalog related to the request. Fixed value: AWSMarketplace

    ", + "location":"querystring", + "locationName":"catalog" + }, + "ChangeSetId":{ + "shape":"ResourceId", + "documentation":"

    Required. The unique identifier for the StartChangeSet request that you want to describe the details for.

    ", + "location":"querystring", + "locationName":"changeSetId" + } + } + }, + "DescribeChangeSetResponse":{ + "type":"structure", + "members":{ + "ChangeSetId":{ + "shape":"ResourceId", + "documentation":"

    Required. The unique identifier for the change set referenced in this request.

    " + }, + "ChangeSetArn":{ + "shape":"ARN", + "documentation":"

    The ARN associated with the unique identifier for the change set referenced in this request.

    " + }, + "ChangeSetName":{ + "shape":"ChangeSetName", + "documentation":"

    The optional name provided in the StartChangeSet request. If you do not provide a name, one is set by default.

    " + }, + "StartTime":{ + "shape":"DateTimeISO8601", + "documentation":"

    The date and time, in ISO 8601 format (2018-02-27T13:45:22Z), the request started.

    " + }, + "EndTime":{ + "shape":"DateTimeISO8601", + "documentation":"

    The date and time, in ISO 8601 format (2018-02-27T13:45:22Z), the request transitioned to a terminal state. The change cannot transition to a different state. Null if the request is not in a terminal state.

    " + }, + "Status":{ + "shape":"ChangeStatus", + "documentation":"

    The status of the change request.

    " + }, + "FailureDescription":{ + "shape":"StringValue", + "documentation":"

    Returned if there is a failure on the change set, but that failure is not related to any of the changes in the request.

    " + }, + "ChangeSet":{ + "shape":"ChangeSetDescription", + "documentation":"

    An array of ChangeSummary objects.

    " + } + } + }, + "DescribeEntityRequest":{ + "type":"structure", + "required":[ + "Catalog", + "EntityId" + ], + "members":{ + "Catalog":{ + "shape":"Catalog", + "documentation":"

    Required. The catalog related to the request. Fixed value: AWSMarketplace

    ", + "location":"querystring", + "locationName":"catalog" + }, + "EntityId":{ + "shape":"ResourceId", + "documentation":"

    Required. The unique ID of the entity to describe.

    ", + "location":"querystring", + "locationName":"entityId" + } + } + }, + "DescribeEntityResponse":{ + "type":"structure", + "members":{ + "EntityType":{ + "shape":"EntityType", + "documentation":"

    The named type of the entity, in the format of EntityType@Version.

    " + }, + "EntityIdentifier":{ + "shape":"Identifier", + "documentation":"

    The identifier of the entity, in the format of EntityId@RevisionId.

    " + }, + "EntityArn":{ + "shape":"ARN", + "documentation":"

    The ARN associated to the unique identifier for the change set referenced in this request.

    " + }, + "LastModifiedDate":{ + "shape":"StringValue", + "documentation":"

    The last modified date of the entity, in ISO 8601 format (2018-02-27T13:45:22Z).

    " + }, + "Details":{ + "shape":"Json", + "documentation":"

    This stringified JSON object includes the details of the entity.

    " + } + } + }, + "Entity":{ + "type":"structure", + "required":["Type"], + "members":{ + "Type":{ + "shape":"EntityType", + "documentation":"

    The type of entity.

    " + }, + "Identifier":{ + "shape":"Identifier", + "documentation":"

    The identifier for the entity.

    " + } + }, + "documentation":"

    A product entity contains data that describes your product, its supported features, and how it can be used or launched by your customer.

    " + }, + "EntitySummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"StringValue", + "documentation":"

    The name for the entity. This value is not unique. It is defined by the provider.

    " + }, + "EntityType":{ + "shape":"EntityType", + "documentation":"

    The type of the entity.

    " + }, + "EntityId":{ + "shape":"ResourceId", + "documentation":"

    The unique identifier for the entity.

    " + }, + "EntityArn":{ + "shape":"ARN", + "documentation":"

    The ARN associated with the unique identifier for the entity.

    " + }, + "LastModifiedDate":{ + "shape":"StringValue", + "documentation":"

    The last time the entity was published, using ISO 8601 format (2018-02-27T13:45:22Z).

    " + }, + "Visibility":{ + "shape":"StringValue", + "documentation":"

    The visibility status of the entity to subscribers. This value can be Public (everyone can view the entity), Limited (the entity is visible to limited accounts only), or Restricted (the entity was published and then unpublished and only existing subscribers can view it).

    " + } + }, + "documentation":"

    This object is a container for common summary information about the entity. The summary doesn't contain the whole entity structure, but it does contain information common across all entities.

    " + }, + "EntitySummaryList":{ + "type":"list", + "member":{"shape":"EntitySummary"} + }, + "EntityType":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[a-zA-Z]+$" + }, + "ErrorDetail":{ + "type":"structure", + "members":{ + "ErrorCode":{ + "shape":"StringValue", + "documentation":"

    The error code that identifies the type of error.

    " + }, + "ErrorMessage":{ + "shape":"StringValue", + "documentation":"

    The message for the error.

    " + } + }, + "documentation":"

    Details about the error.

    " + }, + "ErrorDetailList":{ + "type":"list", + "member":{"shape":"ErrorDetail"} + }, + "Filter":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"FilterName", + "documentation":"

    For ListEntities, the supported value for this is an EntityId.

    For ListChangeSets, the supported values are as follows:

    " + }, + "ValueList":{ + "shape":"ValueList", + "documentation":"

    ListEntities - This is a list of unique EntityIds.

    ListChangeSets - The supported filter names and associated ValueLists is as follows:

    • ChangeSetName - The supported ValueList is a list of non-unique ChangeSetNames. These are defined when you call the StartChangeSet action.

    • Status - The supported ValueList is a list of statuses for all change set requests.

    • EntityId - The supported ValueList is a list of unique EntityIds.

    • BeforeStartTime - The supported ValueList is a list of all change sets that started before the filter value.

    • AfterStartTime - The supported ValueList is a list of all change sets that started after the filter value.

    • BeforeEndTime - The supported ValueList is a list of all change sets that ended before the filter value.

    • AfterEndTime - The supported ValueList is a list of all change sets that ended after the filter value.

    " + } + }, + "documentation":"

    A filter object, used to optionally filter results from calls to the ListEntities and ListChangeSets actions.

    " + }, + "FilterList":{ + "type":"list", + "member":{"shape":"Filter"}, + "max":8, + "min":1 + }, + "FilterName":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[a-zA-Z]+$" + }, + "Identifier":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[\\w\\-@]+$" + }, + "InternalServiceException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    There was an internal service exception.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "synthetic":true + }, + "Json":{ + "type":"string", + "max":16384, + "min":2, + "pattern":"^[\\s]*\\{[\\s\\S]*\\}[\\s]*$" + }, + "ListChangeSetsRequest":{ + "type":"structure", + "required":["Catalog"], + "members":{ + "Catalog":{ + "shape":"Catalog", + "documentation":"

    The catalog related to the request. Fixed value: AWSMarketplace

    " + }, + "FilterList":{ + "shape":"FilterList", + "documentation":"

    An array of filter objects.

    " + }, + "Sort":{ + "shape":"Sort", + "documentation":"

    An object that contains two attributes, sortBy and sortOrder.

    " + }, + "MaxResults":{ + "shape":"MaxResultInteger", + "documentation":"

    The maximum number of results returned by a single call. This value must be provided in the next call to retrieve the next set of results. By default, this value is 20.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The token value retrieved from a previous call to access the next page of results.

    " + } + } + }, + "ListChangeSetsResponse":{ + "type":"structure", + "members":{ + "ChangeSetSummaryList":{ + "shape":"ChangeSetSummaryList", + "documentation":"

    Array of ChangeSetSummaryListItem objects.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The value of the next token, if it exists. Null if there are no more results.

    " + } + } + }, + "ListEntitiesRequest":{ + "type":"structure", + "required":[ + "Catalog", + "EntityType" + ], + "members":{ + "Catalog":{ + "shape":"Catalog", + "documentation":"

    The catalog related to the request. Fixed value: AWSMarketplace

    " + }, + "EntityType":{ + "shape":"EntityType", + "documentation":"

    The type of entities to retrieve.

    " + }, + "FilterList":{ + "shape":"FilterList", + "documentation":"

    An array of filter objects. Each filter object contains two attributes, filterName and filterValues.

    " + }, + "Sort":{ + "shape":"Sort", + "documentation":"

    An object that contains two attributes, sortBy and sortOrder.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The value of the next token, if it exists. Null if there are no more results.

    " + }, + "MaxResults":{ + "shape":"MaxResultInteger", + "documentation":"

    Specifies the upper limit of the elements on a single page. If a value isn't provided, the default value is 20.

    " + } + } + }, + "ListEntitiesResponse":{ + "type":"structure", + "members":{ + "EntitySummaryList":{ + "shape":"EntitySummaryList", + "documentation":"

    Array of EntitySummary object.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    The value of the next token if it exists. Null if there is no more result.

    " + } + } + }, + "MaxResultInteger":{ + "type":"integer", + "box":true, + "max":20, + "min":1 + }, + "NextToken":{ + "type":"string", + "max":2048, + "min":1, + "pattern":"^[\\w+=.:@\\-\\/]$" + }, + "RequestedChangeList":{ + "type":"list", + "member":{"shape":"Change"}, + "max":20, + "min":1 + }, + "ResourceId":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[\\w\\-]+$" + }, + "ResourceIdList":{ + "type":"list", + "member":{"shape":"ResourceId"} + }, + "ResourceInUseException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    The resource is currently in use.

    ", + "error":{"httpStatusCode":423}, + "exception":true, + "synthetic":true + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    The specified resource wasn't found.

    ", + "error":{"httpStatusCode":404}, + "exception":true, + "synthetic":true + }, + "ResourceNotSupportedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    Currently, the specified resource is not supported.

    ", + "error":{"httpStatusCode":415}, + "exception":true, + "synthetic":true + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    The maximum number of open requests per account has been exceeded.

    ", + "error":{"httpStatusCode":402}, + "exception":true, + "synthetic":true + }, + "Sort":{ + "type":"structure", + "members":{ + "SortBy":{ + "shape":"SortBy", + "documentation":"

    For ListEntities, supported attributes include LastModifiedDate (default), Visibility, EntityId, and Name.

    For ListChangeSets, supported attributes include StartTime and EndTime.

    " + }, + "SortOrder":{ + "shape":"SortOrder", + "documentation":"

    The sorting order. Can be ASCENDING or DESCENDING. The default value is DESCENDING.

    " + } + }, + "documentation":"

    An object that contains two attributes, sortBy and sortOrder.

    " + }, + "SortBy":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[a-zA-Z]+$" + }, + "SortOrder":{ + "type":"string", + "enum":[ + "ASCENDING", + "DESCENDING" + ] + }, + "StartChangeSetRequest":{ + "type":"structure", + "required":[ + "Catalog", + "ChangeSet" + ], + "members":{ + "Catalog":{ + "shape":"Catalog", + "documentation":"

    The catalog related to the request. Fixed value: AWSMarketplace

    " + }, + "ChangeSet":{ + "shape":"RequestedChangeList", + "documentation":"

    Array of change object.

    " + }, + "ChangeSetName":{ + "shape":"ChangeSetName", + "documentation":"

    Optional case sensitive string of up to 100 ASCII characters. The change set name can be used to filter the list of change sets.

    " + }, + "ClientRequestToken":{ + "shape":"ClientRequestToken", + "documentation":"

    A unique token to identify the request to ensure idempotency.

    " + } + } + }, + "StartChangeSetResponse":{ + "type":"structure", + "members":{ + "ChangeSetId":{ + "shape":"ResourceId", + "documentation":"

    Unique identifier generated for the request.

    " + }, + "ChangeSetArn":{ + "shape":"ARN", + "documentation":"

    The ARN associated to the unique identifier generated for the request.

    " + } + } + }, + "StringValue":{"type":"string"}, + "ThrottlingException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    Too many requests.

    ", + "error":{"httpStatusCode":429}, + "exception":true, + "synthetic":true + }, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"StringValue"} + }, + "documentation":"

    An error occurred during validation.

    ", + "error":{"httpStatusCode":422}, + "exception":true, + "synthetic":true + }, + "ValueList":{ + "type":"list", + "member":{"shape":"StringValue"}, + "max":10, + "min":1 + } + }, + "documentation":"

    Catalog API actions allow you to create, describe, list, and delete changes to your published entities. An entity is a product or an offer on AWS Marketplace.

    You can automate your entity update process by integrating the AWS Marketplace Catalog API with your AWS Marketplace product build or deployment pipelines. You can also create your own applications on top of the Catalog API to manage your products on AWS Marketplace.

    " +} diff --git a/services/marketplacecommerceanalytics/build.properties b/services/marketplacecommerceanalytics/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/marketplacecommerceanalytics/build.properties +++ b/services/marketplacecommerceanalytics/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/marketplacecommerceanalytics/pom.xml b/services/marketplacecommerceanalytics/pom.xml index 21581dde2cf3..8620447f285b 100644 --- a/services/marketplacecommerceanalytics/pom.xml +++ b/services/marketplacecommerceanalytics/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + migrationhubconfig + AWS Java SDK :: Services :: MigrationHub Config + The AWS Java SDK for MigrationHub Config module holds the client classes that are used for + communicating with MigrationHub Config. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.migrationhubconfig + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/migrationhubconfig/src/main/resources/codegen-resources/paginators-1.json b/services/migrationhubconfig/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..cd89e068e4d1 --- /dev/null +++ b/services/migrationhubconfig/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,9 @@ +{ + "pagination": { + "DescribeHomeRegionControls": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/migrationhubconfig/src/main/resources/codegen-resources/service-2.json b/services/migrationhubconfig/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..7534a7068276 --- /dev/null +++ b/services/migrationhubconfig/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,272 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-06-30", + "endpointPrefix":"migrationhub-config", + "jsonVersion":"1.1", + "protocol":"json", + "serviceFullName":"AWS Migration Hub Config", + "serviceId":"MigrationHub Config", + "signatureVersion":"v4", + "signingName":"mgh", + "targetPrefix":"AWSMigrationHubMultiAccountService", + "uid":"migrationhub-config-2019-06-30" + }, + "operations":{ + "CreateHomeRegionControl":{ + "name":"CreateHomeRegionControl", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateHomeRegionControlRequest"}, + "output":{"shape":"CreateHomeRegionControlResult"}, + "errors":[ + {"shape":"InternalServerError"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"DryRunOperation"}, + {"shape":"InvalidInputException"} + ], + "documentation":"

    This API sets up the home region for the calling account only.

    " + }, + "DescribeHomeRegionControls":{ + "name":"DescribeHomeRegionControls", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeHomeRegionControlsRequest"}, + "output":{"shape":"DescribeHomeRegionControlsResult"}, + "errors":[ + {"shape":"InternalServerError"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidInputException"} + ], + "documentation":"

    This API permits filtering on the ControlId, HomeRegion, and RegionControlScope fields.

    " + }, + "GetHomeRegion":{ + "name":"GetHomeRegion", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetHomeRegionRequest"}, + "output":{"shape":"GetHomeRegionResult"}, + "errors":[ + {"shape":"InternalServerError"}, + {"shape":"ServiceUnavailableException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InvalidInputException"} + ], + "documentation":"

    Returns the calling account’s home region, if configured. This API is used by other AWS services to determine the regional endpoint for calling AWS Application Discovery Service and Migration Hub. You must call GetHomeRegion at least once before you call any other AWS Application Discovery Service and AWS Migration Hub APIs, to obtain the account's Migration Hub home region.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "exception":true + }, + "ControlId":{ + "type":"string", + "max":50, + "min":1, + "pattern":"^hrc-[a-z0-9]{12}$" + }, + "CreateHomeRegionControlRequest":{ + "type":"structure", + "required":[ + "HomeRegion", + "Target" + ], + "members":{ + "HomeRegion":{ + "shape":"HomeRegion", + "documentation":"

    The name of the home region of the calling account.

    " + }, + "Target":{ + "shape":"Target", + "documentation":"

    The account for which this command sets up a home region control. The Target is always of type ACCOUNT.

    " + }, + "DryRun":{ + "shape":"DryRun", + "documentation":"

    Optional Boolean flag to indicate whether any effect should take place. It tests whether the caller has permission to make the call.

    " + } + } + }, + "CreateHomeRegionControlResult":{ + "type":"structure", + "members":{ + "HomeRegionControl":{ + "shape":"HomeRegionControl", + "documentation":"

    This object is the HomeRegionControl object that's returned by a successful call to CreateHomeRegionControl.

    " + } + } + }, + "DescribeHomeRegionControlsMaxResults":{ + "type":"integer", + "box":true, + "max":100, + "min":1 + }, + "DescribeHomeRegionControlsRequest":{ + "type":"structure", + "members":{ + "ControlId":{ + "shape":"ControlId", + "documentation":"

    The ControlID is a unique identifier string of your HomeRegionControl object.

    " + }, + "HomeRegion":{ + "shape":"HomeRegion", + "documentation":"

    The name of the home region you'd like to view.

    " + }, + "Target":{ + "shape":"Target", + "documentation":"

    The target parameter specifies the identifier to which the home region is applied, which is always of type ACCOUNT. It applies the home region to the current ACCOUNT.

    " + }, + "MaxResults":{ + "shape":"DescribeHomeRegionControlsMaxResults", + "documentation":"

    The maximum number of filtering results to display per page.

    " + }, + "NextToken":{ + "shape":"Token", + "documentation":"

    If a NextToken was returned by a previous call, more results are available. To retrieve the next page of results, make the call again using the returned token in NextToken.

    " + } + } + }, + "DescribeHomeRegionControlsResult":{ + "type":"structure", + "members":{ + "HomeRegionControls":{ + "shape":"HomeRegionControls", + "documentation":"

    An array that contains your HomeRegionControl objects.

    " + }, + "NextToken":{ + "shape":"Token", + "documentation":"

    If a NextToken was returned by a previous call, more results are available. To retrieve the next page of results, make the call again using the returned token in NextToken.

    " + } + } + }, + "DryRun":{"type":"boolean"}, + "DryRunOperation":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Exception raised to indicate that authorization of an action was successful, when the DryRun flag is set to true.

    ", + "exception":true + }, + "ErrorMessage":{"type":"string"}, + "GetHomeRegionRequest":{ + "type":"structure", + "members":{ + } + }, + "GetHomeRegionResult":{ + "type":"structure", + "members":{ + "HomeRegion":{ + "shape":"HomeRegion", + "documentation":"

    The name of the home region of the calling account.

    " + } + } + }, + "HomeRegion":{ + "type":"string", + "max":50, + "min":1, + "pattern":"^([a-z]+)-([a-z]+)-([0-9]+)$" + }, + "HomeRegionControl":{ + "type":"structure", + "members":{ + "ControlId":{ + "shape":"ControlId", + "documentation":"

    A unique identifier that's generated for each home region control. It's always a string that begins with \"hrc-\" followed by 12 lowercase letters and numbers.

    " + }, + "HomeRegion":{ + "shape":"HomeRegion", + "documentation":"

    The AWS Region that's been set as home region. For example, \"us-west-2\" or \"eu-central-1\" are valid home regions.

    " + }, + "Target":{ + "shape":"Target", + "documentation":"

    The target parameter specifies the identifier to which the home region is applied, which is always an ACCOUNT. It applies the home region to the current ACCOUNT.

    " + }, + "RequestedTime":{ + "shape":"RequestedTime", + "documentation":"

    A timestamp representing the time when the customer called CreateHomeregionControl and set the home region for the account.

    " + } + }, + "documentation":"

    A home region control is an object that specifies the home region for an account, with some additional information. It contains a target (always of type ACCOUNT), an ID, and a time at which the home region was set.

    " + }, + "HomeRegionControls":{ + "type":"list", + "member":{"shape":"HomeRegionControl"}, + "max":100 + }, + "InternalServerError":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Exception raised when an internal, configuration, or dependency error is encountered.

    ", + "exception":true, + "fault":true + }, + "InvalidInputException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Exception raised when the provided input violates a policy constraint or is entered in the wrong format or data type.

    ", + "exception":true + }, + "RequestedTime":{"type":"timestamp"}, + "ServiceUnavailableException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Exception raised when a request fails due to temporary unavailability of the service.

    ", + "exception":true, + "fault":true + }, + "Target":{ + "type":"structure", + "required":["Type"], + "members":{ + "Type":{ + "shape":"TargetType", + "documentation":"

    The target type is always an ACCOUNT.

    " + }, + "Id":{ + "shape":"TargetId", + "documentation":"

    The TargetID is a 12-character identifier of the ACCOUNT for which the control was created. (This must be the current account.)

    " + } + }, + "documentation":"

    The target parameter specifies the identifier to which the home region is applied, which is always an ACCOUNT. It applies the home region to the current ACCOUNT.

    " + }, + "TargetId":{ + "type":"string", + "max":12, + "min":12, + "pattern":"^\\d{12}$" + }, + "TargetType":{ + "type":"string", + "enum":["ACCOUNT"] + }, + "Token":{ + "type":"string", + "max":2048, + "min":0, + "pattern":"^[a-zA-Z0-9\\/\\+\\=]{0,2048}$" + } + }, + "documentation":"

    The AWS Migration Hub home region APIs are available specifically for working with your Migration Hub home region. You can use these APIs to determine a home region, as well as to create and work with controls that describe the home region.

    You can use these APIs within your home region only. If you call these APIs from outside your home region, your calls are rejected, except for the ability to register your agents and connectors.

    You must call GetHomeRegion at least once before you call any other AWS Application Discovery Service and AWS Migration Hub APIs, to obtain the account's Migration Hub home region.

    The StartDataCollection API call in AWS Application Discovery Service allows your agents and connectors to begin collecting data that flows directly into the home region, and it will prevent you from enabling data collection information to be sent outside the home region.

    For specific API usage, see the sections that follow in this AWS Migration Hub Home Region API reference.

    The Migration Hub Home Region APIs do not support AWS Organizations.

    " +} diff --git a/services/mobile/pom.xml b/services/mobile/pom.xml index 7d8c6cb23824..c0346127574c 100644 --- a/services/mobile/pom.xml +++ b/services/mobile/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + networkmanager + AWS Java SDK :: Services :: NetworkManager + The AWS Java SDK for NetworkManager module holds the client classes that are used for + communicating with NetworkManager. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.networkmanager + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/networkmanager/src/main/resources/codegen-resources/paginators-1.json b/services/networkmanager/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..37b8fedd68d2 --- /dev/null +++ b/services/networkmanager/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,46 @@ +{ + "pagination": { + "DescribeGlobalNetworks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "GlobalNetworks" + }, + "GetCustomerGatewayAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "CustomerGatewayAssociations" + }, + "GetDevices": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Devices" + }, + "GetLinkAssociations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "LinkAssociations" + }, + "GetLinks": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Links" + }, + "GetSites": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "Sites" + }, + "GetTransitGatewayRegistrations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "TransitGatewayRegistrations" + } + } +} diff --git a/services/networkmanager/src/main/resources/codegen-resources/service-2.json b/services/networkmanager/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..5912f67de933 --- /dev/null +++ b/services/networkmanager/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,2133 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-07-05", + "endpointPrefix":"networkmanager", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"NetworkManager", + "serviceFullName":"AWS Network Manager", + "serviceId":"NetworkManager", + "signatureVersion":"v4", + "signingName":"networkmanager", + "uid":"networkmanager-2019-07-05" + }, + "operations":{ + "AssociateCustomerGateway":{ + "name":"AssociateCustomerGateway", + "http":{ + "method":"POST", + "requestUri":"/global-networks/{globalNetworkId}/customer-gateway-associations" + }, + "input":{"shape":"AssociateCustomerGatewayRequest"}, + "output":{"shape":"AssociateCustomerGatewayResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Associates a customer gateway with a device and optionally, with a link. If you specify a link, it must be associated with the specified device.

    You can only associate customer gateways that are connected to a VPN attachment on a transit gateway. The transit gateway must be registered in your global network. When you register a transit gateway, customer gateways that are connected to the transit gateway are automatically included in the global network. To list customer gateways that are connected to a transit gateway, use the DescribeVpnConnections EC2 API and filter by transit-gateway-id.

    You cannot associate a customer gateway with more than one device and link.

    " + }, + "AssociateLink":{ + "name":"AssociateLink", + "http":{ + "method":"POST", + "requestUri":"/global-networks/{globalNetworkId}/link-associations" + }, + "input":{"shape":"AssociateLinkRequest"}, + "output":{"shape":"AssociateLinkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Associates a link to a device. A device can be associated to multiple links and a link can be associated to multiple devices. The device and link must be in the same global network and the same site.

    " + }, + "CreateDevice":{ + "name":"CreateDevice", + "http":{ + "method":"POST", + "requestUri":"/global-networks/{globalNetworkId}/devices" + }, + "input":{"shape":"CreateDeviceRequest"}, + "output":{"shape":"CreateDeviceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates a new device in a global network. If you specify both a site ID and a location, the location of the site is used for visualization in the Network Manager console.

    " + }, + "CreateGlobalNetwork":{ + "name":"CreateGlobalNetwork", + "http":{ + "method":"POST", + "requestUri":"/global-networks" + }, + "input":{"shape":"CreateGlobalNetworkRequest"}, + "output":{"shape":"CreateGlobalNetworkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates a new, empty global network.

    " + }, + "CreateLink":{ + "name":"CreateLink", + "http":{ + "method":"POST", + "requestUri":"/global-networks/{globalNetworkId}/links" + }, + "input":{"shape":"CreateLinkRequest"}, + "output":{"shape":"CreateLinkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates a new link for a specified site.

    " + }, + "CreateSite":{ + "name":"CreateSite", + "http":{ + "method":"POST", + "requestUri":"/global-networks/{globalNetworkId}/sites" + }, + "input":{"shape":"CreateSiteRequest"}, + "output":{"shape":"CreateSiteResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates a new site in a global network.

    " + }, + "DeleteDevice":{ + "name":"DeleteDevice", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}/devices/{deviceId}" + }, + "input":{"shape":"DeleteDeviceRequest"}, + "output":{"shape":"DeleteDeviceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes an existing device. You must first disassociate the device from any links and customer gateways.

    " + }, + "DeleteGlobalNetwork":{ + "name":"DeleteGlobalNetwork", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}" + }, + "input":{"shape":"DeleteGlobalNetworkRequest"}, + "output":{"shape":"DeleteGlobalNetworkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes an existing global network. You must first delete all global network objects (devices, links, and sites) and deregister all transit gateways.

    " + }, + "DeleteLink":{ + "name":"DeleteLink", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}/links/{linkId}" + }, + "input":{"shape":"DeleteLinkRequest"}, + "output":{"shape":"DeleteLinkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes an existing link. You must first disassociate the link from any devices and customer gateways.

    " + }, + "DeleteSite":{ + "name":"DeleteSite", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}/sites/{siteId}" + }, + "input":{"shape":"DeleteSiteRequest"}, + "output":{"shape":"DeleteSiteResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes an existing site. The site cannot be associated with any device or link.

    " + }, + "DeregisterTransitGateway":{ + "name":"DeregisterTransitGateway", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}/transit-gateway-registrations/{transitGatewayArn}" + }, + "input":{"shape":"DeregisterTransitGatewayRequest"}, + "output":{"shape":"DeregisterTransitGatewayResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deregisters a transit gateway from your global network. This action does not delete your transit gateway, or modify any of its attachments. This action removes any customer gateway associations.

    " + }, + "DescribeGlobalNetworks":{ + "name":"DescribeGlobalNetworks", + "http":{ + "method":"GET", + "requestUri":"/global-networks" + }, + "input":{"shape":"DescribeGlobalNetworksRequest"}, + "output":{"shape":"DescribeGlobalNetworksResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Describes one or more global networks. By default, all global networks are described. To describe the objects in your global network, you must use the appropriate Get* action. For example, to list the transit gateways in your global network, use GetTransitGatewayRegistrations.

    " + }, + "DisassociateCustomerGateway":{ + "name":"DisassociateCustomerGateway", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}/customer-gateway-associations/{customerGatewayArn}" + }, + "input":{"shape":"DisassociateCustomerGatewayRequest"}, + "output":{"shape":"DisassociateCustomerGatewayResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Disassociates a customer gateway from a device and a link.

    " + }, + "DisassociateLink":{ + "name":"DisassociateLink", + "http":{ + "method":"DELETE", + "requestUri":"/global-networks/{globalNetworkId}/link-associations" + }, + "input":{"shape":"DisassociateLinkRequest"}, + "output":{"shape":"DisassociateLinkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Disassociates an existing device from a link. You must first disassociate any customer gateways that are associated with the link.

    " + }, + "GetCustomerGatewayAssociations":{ + "name":"GetCustomerGatewayAssociations", + "http":{ + "method":"GET", + "requestUri":"/global-networks/{globalNetworkId}/customer-gateway-associations" + }, + "input":{"shape":"GetCustomerGatewayAssociationsRequest"}, + "output":{"shape":"GetCustomerGatewayAssociationsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets the association information for customer gateways that are associated with devices and links in your global network.

    " + }, + "GetDevices":{ + "name":"GetDevices", + "http":{ + "method":"GET", + "requestUri":"/global-networks/{globalNetworkId}/devices" + }, + "input":{"shape":"GetDevicesRequest"}, + "output":{"shape":"GetDevicesResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about one or more of your devices in a global network.

    " + }, + "GetLinkAssociations":{ + "name":"GetLinkAssociations", + "http":{ + "method":"GET", + "requestUri":"/global-networks/{globalNetworkId}/link-associations" + }, + "input":{"shape":"GetLinkAssociationsRequest"}, + "output":{"shape":"GetLinkAssociationsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets the link associations for a device or a link. Either the device ID or the link ID must be specified.

    " + }, + "GetLinks":{ + "name":"GetLinks", + "http":{ + "method":"GET", + "requestUri":"/global-networks/{globalNetworkId}/links" + }, + "input":{"shape":"GetLinksRequest"}, + "output":{"shape":"GetLinksResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about one or more links in a specified global network.

    If you specify the site ID, you cannot specify the type or provider in the same request. You can specify the type and provider in the same request.

    " + }, + "GetSites":{ + "name":"GetSites", + "http":{ + "method":"GET", + "requestUri":"/global-networks/{globalNetworkId}/sites" + }, + "input":{"shape":"GetSitesRequest"}, + "output":{"shape":"GetSitesResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about one or more of your sites in a global network.

    " + }, + "GetTransitGatewayRegistrations":{ + "name":"GetTransitGatewayRegistrations", + "http":{ + "method":"GET", + "requestUri":"/global-networks/{globalNetworkId}/transit-gateway-registrations" + }, + "input":{"shape":"GetTransitGatewayRegistrationsRequest"}, + "output":{"shape":"GetTransitGatewayRegistrationsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about the transit gateway registrations in a specified global network.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"GET", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Lists the tags for a specified resource.

    " + }, + "RegisterTransitGateway":{ + "name":"RegisterTransitGateway", + "http":{ + "method":"POST", + "requestUri":"/global-networks/{globalNetworkId}/transit-gateway-registrations" + }, + "input":{"shape":"RegisterTransitGatewayRequest"}, + "output":{"shape":"RegisterTransitGatewayResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Registers a transit gateway in your global network. The transit gateway can be in any AWS Region, but it must be owned by the same AWS account that owns the global network. You cannot register a transit gateway in more than one global network.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Tags a specified resource.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"DELETE", + "requestUri":"/tags/{resourceArn}" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Removes tags from a specified resource.

    " + }, + "UpdateDevice":{ + "name":"UpdateDevice", + "http":{ + "method":"PATCH", + "requestUri":"/global-networks/{globalNetworkId}/devices/{deviceId}" + }, + "input":{"shape":"UpdateDeviceRequest"}, + "output":{"shape":"UpdateDeviceResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates the details for an existing device. To remove information for any of the parameters, specify an empty string.

    " + }, + "UpdateGlobalNetwork":{ + "name":"UpdateGlobalNetwork", + "http":{ + "method":"PATCH", + "requestUri":"/global-networks/{globalNetworkId}" + }, + "input":{"shape":"UpdateGlobalNetworkRequest"}, + "output":{"shape":"UpdateGlobalNetworkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates an existing global network. To remove information for any of the parameters, specify an empty string.

    " + }, + "UpdateLink":{ + "name":"UpdateLink", + "http":{ + "method":"PATCH", + "requestUri":"/global-networks/{globalNetworkId}/links/{linkId}" + }, + "input":{"shape":"UpdateLinkRequest"}, + "output":{"shape":"UpdateLinkResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates the details for an existing link. To remove information for any of the parameters, specify an empty string.

    " + }, + "UpdateSite":{ + "name":"UpdateSite", + "http":{ + "method":"PATCH", + "requestUri":"/global-networks/{globalNetworkId}/sites/{siteId}" + }, + "input":{"shape":"UpdateSiteRequest"}, + "output":{"shape":"UpdateSiteResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ConflictException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Updates the information for an existing site. To remove information for any of the parameters, specify an empty string.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"String"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "error":{"httpStatusCode":403}, + "exception":true + }, + "AssociateCustomerGatewayRequest":{ + "type":"structure", + "required":[ + "CustomerGatewayArn", + "GlobalNetworkId", + "DeviceId" + ], + "members":{ + "CustomerGatewayArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the customer gateway. For more information, see Resources Defined by Amazon EC2.

    " + }, + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    " + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    " + } + } + }, + "AssociateCustomerGatewayResponse":{ + "type":"structure", + "members":{ + "CustomerGatewayAssociation":{ + "shape":"CustomerGatewayAssociation", + "documentation":"

    The customer gateway association.

    " + } + } + }, + "AssociateLinkRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "DeviceId", + "LinkId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    " + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    " + } + } + }, + "AssociateLinkResponse":{ + "type":"structure", + "members":{ + "LinkAssociation":{ + "shape":"LinkAssociation", + "documentation":"

    The link association.

    " + } + } + }, + "Bandwidth":{ + "type":"structure", + "members":{ + "UploadSpeed":{ + "shape":"Integer", + "documentation":"

    Upload speed in Mbps.

    " + }, + "DownloadSpeed":{ + "shape":"Integer", + "documentation":"

    Download speed in Mbps.

    " + } + }, + "documentation":"

    Describes bandwidth information.

    " + }, + "ConflictException":{ + "type":"structure", + "required":[ + "Message", + "ResourceId", + "ResourceType" + ], + "members":{ + "Message":{"shape":"String"}, + "ResourceId":{ + "shape":"String", + "documentation":"

    The ID of the resource.

    " + }, + "ResourceType":{ + "shape":"String", + "documentation":"

    The resource type.

    " + } + }, + "documentation":"

    There was a conflict processing the request. Updating or deleting the resource can cause an inconsistent state.

    ", + "error":{"httpStatusCode":409}, + "exception":true + }, + "CreateDeviceRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of the device.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Type":{ + "shape":"String", + "documentation":"

    The type of the device.

    " + }, + "Vendor":{ + "shape":"String", + "documentation":"

    The vendor of the device.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Model":{ + "shape":"String", + "documentation":"

    The model of the device.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "SerialNumber":{ + "shape":"String", + "documentation":"

    The serial number of the device.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Location":{ + "shape":"Location", + "documentation":"

    The location of the device.

    " + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags to apply to the resource during creation.

    " + } + } + }, + "CreateDeviceResponse":{ + "type":"structure", + "members":{ + "Device":{ + "shape":"Device", + "documentation":"

    Information about the device.

    " + } + } + }, + "CreateGlobalNetworkRequest":{ + "type":"structure", + "members":{ + "Description":{ + "shape":"String", + "documentation":"

    A description of the global network.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags to apply to the resource during creation.

    " + } + } + }, + "CreateGlobalNetworkResponse":{ + "type":"structure", + "members":{ + "GlobalNetwork":{ + "shape":"GlobalNetwork", + "documentation":"

    Information about the global network object.

    " + } + } + }, + "CreateLinkRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "Bandwidth", + "SiteId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of the link.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Type":{ + "shape":"String", + "documentation":"

    The type of the link.

    Constraints: Cannot include the following characters: | \\ ^

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Bandwidth":{ + "shape":"Bandwidth", + "documentation":"

    The upload speed and download speed in Mbps.

    " + }, + "Provider":{ + "shape":"String", + "documentation":"

    The provider of the link.

    Constraints: Cannot include the following characters: | \\ ^

    Length Constraints: Maximum length of 128 characters.

    " + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags to apply to the resource during creation.

    " + } + } + }, + "CreateLinkResponse":{ + "type":"structure", + "members":{ + "Link":{ + "shape":"Link", + "documentation":"

    Information about the link.

    " + } + } + }, + "CreateSiteRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of your site.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Location":{ + "shape":"Location", + "documentation":"

    The site location. This information is used for visualization in the Network Manager console. If you specify the address, the latitude and longitude are automatically calculated.

    • Address: The physical address of the site.

    • Latitude: The latitude of the site.

    • Longitude: The longitude of the site.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags to apply to the resource during creation.

    " + } + } + }, + "CreateSiteResponse":{ + "type":"structure", + "members":{ + "Site":{ + "shape":"Site", + "documentation":"

    Information about the site.

    " + } + } + }, + "CustomerGatewayAssociation":{ + "type":"structure", + "members":{ + "CustomerGatewayArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the customer gateway.

    " + }, + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    " + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    " + }, + "State":{ + "shape":"CustomerGatewayAssociationState", + "documentation":"

    The association state.

    " + } + }, + "documentation":"

    Describes the association between a customer gateway, a device, and a link.

    " + }, + "CustomerGatewayAssociationList":{ + "type":"list", + "member":{"shape":"CustomerGatewayAssociation"} + }, + "CustomerGatewayAssociationState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "DELETED" + ] + }, + "DateTime":{"type":"timestamp"}, + "DeleteDeviceRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "DeviceId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    ", + "location":"uri", + "locationName":"deviceId" + } + } + }, + "DeleteDeviceResponse":{ + "type":"structure", + "members":{ + "Device":{ + "shape":"Device", + "documentation":"

    Information about the device.

    " + } + } + }, + "DeleteGlobalNetworkRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + } + } + }, + "DeleteGlobalNetworkResponse":{ + "type":"structure", + "members":{ + "GlobalNetwork":{ + "shape":"GlobalNetwork", + "documentation":"

    Information about the global network.

    " + } + } + }, + "DeleteLinkRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "LinkId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    ", + "location":"uri", + "locationName":"linkId" + } + } + }, + "DeleteLinkResponse":{ + "type":"structure", + "members":{ + "Link":{ + "shape":"Link", + "documentation":"

    Information about the link.

    " + } + } + }, + "DeleteSiteRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "SiteId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    ", + "location":"uri", + "locationName":"siteId" + } + } + }, + "DeleteSiteResponse":{ + "type":"structure", + "members":{ + "Site":{ + "shape":"Site", + "documentation":"

    Information about the site.

    " + } + } + }, + "DeregisterTransitGatewayRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "TransitGatewayArn" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "TransitGatewayArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the transit gateway.

    ", + "location":"uri", + "locationName":"transitGatewayArn" + } + } + }, + "DeregisterTransitGatewayResponse":{ + "type":"structure", + "members":{ + "TransitGatewayRegistration":{ + "shape":"TransitGatewayRegistration", + "documentation":"

    The transit gateway registration information.

    " + } + } + }, + "DescribeGlobalNetworksRequest":{ + "type":"structure", + "members":{ + "GlobalNetworkIds":{ + "shape":"StringList", + "documentation":"

    The IDs of one or more global networks. The maximum is 10.

    ", + "location":"querystring", + "locationName":"globalNetworkIds" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "DescribeGlobalNetworksResponse":{ + "type":"structure", + "members":{ + "GlobalNetworks":{ + "shape":"GlobalNetworkList", + "documentation":"

    Information about the global networks.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "Device":{ + "type":"structure", + "members":{ + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    " + }, + "DeviceArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the device.

    " + }, + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "Description":{ + "shape":"String", + "documentation":"

    The description of the device.

    " + }, + "Type":{ + "shape":"String", + "documentation":"

    The device type.

    " + }, + "Vendor":{ + "shape":"String", + "documentation":"

    The device vendor.

    " + }, + "Model":{ + "shape":"String", + "documentation":"

    The device model.

    " + }, + "SerialNumber":{ + "shape":"String", + "documentation":"

    The device serial number.

    " + }, + "Location":{ + "shape":"Location", + "documentation":"

    The site location.

    " + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The site ID.

    " + }, + "CreatedAt":{ + "shape":"DateTime", + "documentation":"

    The date and time that the site was created.

    " + }, + "State":{ + "shape":"DeviceState", + "documentation":"

    The device state.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags for the device.

    " + } + }, + "documentation":"

    Describes a device.

    " + }, + "DeviceList":{ + "type":"list", + "member":{"shape":"Device"} + }, + "DeviceState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "UPDATING" + ] + }, + "DisassociateCustomerGatewayRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "CustomerGatewayArn" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "CustomerGatewayArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the customer gateway. For more information, see Resources Defined by Amazon EC2.

    ", + "location":"uri", + "locationName":"customerGatewayArn" + } + } + }, + "DisassociateCustomerGatewayResponse":{ + "type":"structure", + "members":{ + "CustomerGatewayAssociation":{ + "shape":"CustomerGatewayAssociation", + "documentation":"

    Information about the customer gateway association.

    " + } + } + }, + "DisassociateLinkRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "DeviceId", + "LinkId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    ", + "location":"querystring", + "locationName":"deviceId" + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    ", + "location":"querystring", + "locationName":"linkId" + } + } + }, + "DisassociateLinkResponse":{ + "type":"structure", + "members":{ + "LinkAssociation":{ + "shape":"LinkAssociation", + "documentation":"

    Information about the link association.

    " + } + } + }, + "GetCustomerGatewayAssociationsRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "CustomerGatewayArns":{ + "shape":"StringList", + "documentation":"

    One or more customer gateway Amazon Resource Names (ARNs). For more information, see Resources Defined by Amazon EC2. The maximum is 10.

    ", + "location":"querystring", + "locationName":"customerGatewayArns" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "GetCustomerGatewayAssociationsResponse":{ + "type":"structure", + "members":{ + "CustomerGatewayAssociations":{ + "shape":"CustomerGatewayAssociationList", + "documentation":"

    The customer gateway associations.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "GetDevicesRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceIds":{ + "shape":"StringList", + "documentation":"

    One or more device IDs. The maximum is 10.

    ", + "location":"querystring", + "locationName":"deviceIds" + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    ", + "location":"querystring", + "locationName":"siteId" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "GetDevicesResponse":{ + "type":"structure", + "members":{ + "Devices":{ + "shape":"DeviceList", + "documentation":"

    The devices.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "GetLinkAssociationsRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    ", + "location":"querystring", + "locationName":"deviceId" + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    ", + "location":"querystring", + "locationName":"linkId" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "GetLinkAssociationsResponse":{ + "type":"structure", + "members":{ + "LinkAssociations":{ + "shape":"LinkAssociationList", + "documentation":"

    The link associations.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "GetLinksRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "LinkIds":{ + "shape":"StringList", + "documentation":"

    One or more link IDs. The maximum is 10.

    ", + "location":"querystring", + "locationName":"linkIds" + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    ", + "location":"querystring", + "locationName":"siteId" + }, + "Type":{ + "shape":"String", + "documentation":"

    The link type.

    ", + "location":"querystring", + "locationName":"type" + }, + "Provider":{ + "shape":"String", + "documentation":"

    The link provider.

    ", + "location":"querystring", + "locationName":"provider" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "GetLinksResponse":{ + "type":"structure", + "members":{ + "Links":{ + "shape":"LinkList", + "documentation":"

    The links.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "GetSitesRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "SiteIds":{ + "shape":"StringList", + "documentation":"

    One or more site IDs. The maximum is 10.

    ", + "location":"querystring", + "locationName":"siteIds" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "GetSitesResponse":{ + "type":"structure", + "members":{ + "Sites":{ + "shape":"SiteList", + "documentation":"

    The sites.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "GetTransitGatewayRegistrationsRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "TransitGatewayArns":{ + "shape":"StringList", + "documentation":"

    The Amazon Resource Names (ARNs) of one or more transit gateways. The maximum is 10.

    ", + "location":"querystring", + "locationName":"transitGatewayArns" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return.

    ", + "location":"querystring", + "locationName":"maxResults" + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + } + } + }, + "GetTransitGatewayRegistrationsResponse":{ + "type":"structure", + "members":{ + "TransitGatewayRegistrations":{ + "shape":"TransitGatewayRegistrationList", + "documentation":"

    The transit gateway registrations.

    " + }, + "NextToken":{ + "shape":"String", + "documentation":"

    The token for the next page of results.

    " + } + } + }, + "GlobalNetwork":{ + "type":"structure", + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "GlobalNetworkArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the global network.

    " + }, + "Description":{ + "shape":"String", + "documentation":"

    The description of the global network.

    " + }, + "CreatedAt":{ + "shape":"DateTime", + "documentation":"

    The date and time that the global network was created.

    " + }, + "State":{ + "shape":"GlobalNetworkState", + "documentation":"

    The state of the global network.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags for the global network.

    " + } + }, + "documentation":"

    Describes a global network.

    " + }, + "GlobalNetworkList":{ + "type":"list", + "member":{"shape":"GlobalNetwork"} + }, + "GlobalNetworkState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "UPDATING" + ] + }, + "Integer":{"type":"integer"}, + "InternalServerException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"String"}, + "RetryAfterSeconds":{ + "shape":"RetryAfterSeconds", + "documentation":"

    Indicates when to retry the request.

    ", + "location":"header", + "locationName":"Retry-After" + } + }, + "documentation":"

    The request has failed due to an internal error.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "Link":{ + "type":"structure", + "members":{ + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    " + }, + "LinkArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the link.

    " + }, + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    " + }, + "Description":{ + "shape":"String", + "documentation":"

    The description of the link.

    " + }, + "Type":{ + "shape":"String", + "documentation":"

    The type of the link.

    " + }, + "Bandwidth":{ + "shape":"Bandwidth", + "documentation":"

    The bandwidth for the link.

    " + }, + "Provider":{ + "shape":"String", + "documentation":"

    The provider of the link.

    " + }, + "CreatedAt":{ + "shape":"DateTime", + "documentation":"

    The date and time that the link was created.

    " + }, + "State":{ + "shape":"LinkState", + "documentation":"

    The state of the link.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags for the link.

    " + } + }, + "documentation":"

    Describes a link.

    " + }, + "LinkAssociation":{ + "type":"structure", + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The device ID for the link association.

    " + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    " + }, + "LinkAssociationState":{ + "shape":"LinkAssociationState", + "documentation":"

    The state of the association.

    " + } + }, + "documentation":"

    Describes the association between a device and a link.

    " + }, + "LinkAssociationList":{ + "type":"list", + "member":{"shape":"LinkAssociation"} + }, + "LinkAssociationState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "DELETED" + ] + }, + "LinkList":{ + "type":"list", + "member":{"shape":"Link"} + }, + "LinkState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "UPDATING" + ] + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceARN", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    ", + "location":"uri", + "locationName":"resourceArn" + } + } + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "members":{ + "TagList":{ + "shape":"TagList", + "documentation":"

    The list of tags.

    " + } + } + }, + "Location":{ + "type":"structure", + "members":{ + "Address":{ + "shape":"String", + "documentation":"

    The physical address.

    " + }, + "Latitude":{ + "shape":"String", + "documentation":"

    The latitude.

    " + }, + "Longitude":{ + "shape":"String", + "documentation":"

    The longitude.

    " + } + }, + "documentation":"

    Describes a location.

    " + }, + "MaxResults":{ + "type":"integer", + "max":500, + "min":1 + }, + "RegisterTransitGatewayRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "TransitGatewayArn" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "TransitGatewayArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the transit gateway. For more information, see Resources Defined by Amazon EC2.

    " + } + } + }, + "RegisterTransitGatewayResponse":{ + "type":"structure", + "members":{ + "TransitGatewayRegistration":{ + "shape":"TransitGatewayRegistration", + "documentation":"

    Information about the transit gateway registration.

    " + } + } + }, + "ResourceARN":{"type":"string"}, + "ResourceNotFoundException":{ + "type":"structure", + "required":[ + "Message", + "ResourceId", + "ResourceType" + ], + "members":{ + "Message":{"shape":"String"}, + "ResourceId":{ + "shape":"String", + "documentation":"

    The ID of the resource.

    " + }, + "ResourceType":{ + "shape":"String", + "documentation":"

    The resource type.

    " + } + }, + "documentation":"

    The specified resource could not be found.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "RetryAfterSeconds":{"type":"integer"}, + "ServiceQuotaExceededException":{ + "type":"structure", + "required":[ + "Message", + "LimitCode", + "ServiceCode" + ], + "members":{ + "Message":{ + "shape":"String", + "documentation":"

    The error message.

    " + }, + "ResourceId":{ + "shape":"String", + "documentation":"

    The ID of the resource.

    " + }, + "ResourceType":{ + "shape":"String", + "documentation":"

    The resource type.

    " + }, + "LimitCode":{ + "shape":"String", + "documentation":"

    The limit code.

    " + }, + "ServiceCode":{ + "shape":"String", + "documentation":"

    The service code.

    " + } + }, + "documentation":"

    A service limit was exceeded.

    ", + "error":{"httpStatusCode":402}, + "exception":true + }, + "Site":{ + "type":"structure", + "members":{ + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    " + }, + "SiteArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the site.

    " + }, + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "Description":{ + "shape":"String", + "documentation":"

    The description of the site.

    " + }, + "Location":{ + "shape":"Location", + "documentation":"

    The location of the site.

    " + }, + "CreatedAt":{ + "shape":"DateTime", + "documentation":"

    The date and time that the site was created.

    " + }, + "State":{ + "shape":"SiteState", + "documentation":"

    The state of the site.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags for the site.

    " + } + }, + "documentation":"

    Describes a site.

    " + }, + "SiteList":{ + "type":"list", + "member":{"shape":"Site"} + }, + "SiteState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "UPDATING" + ] + }, + "String":{"type":"string"}, + "StringList":{ + "type":"list", + "member":{"shape":"String"} + }, + "Tag":{ + "type":"structure", + "members":{ + "Key":{ + "shape":"TagKey", + "documentation":"

    The tag key.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Value":{ + "shape":"TagValue", + "documentation":"

    The tag value.

    Length Constraints: Maximum length of 256 characters.

    " + } + }, + "documentation":"

    Describes a tag.

    " + }, + "TagKey":{"type":"string"}, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"} + }, + "TagList":{ + "type":"list", + "member":{"shape":"Tag"} + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "Tags" + ], + "members":{ + "ResourceArn":{ + "shape":"ResourceARN", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    ", + "location":"uri", + "locationName":"resourceArn" + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    The tags to apply to the specified resource.

    " + } + } + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{"type":"string"}, + "ThrottlingException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"String"}, + "RetryAfterSeconds":{ + "shape":"RetryAfterSeconds", + "documentation":"

    Indicates when to retry the request.

    ", + "location":"header", + "locationName":"Retry-After" + } + }, + "documentation":"

    The request was denied due to request throttling.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "TransitGatewayRegistration":{ + "type":"structure", + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    " + }, + "TransitGatewayArn":{ + "shape":"String", + "documentation":"

    The Amazon Resource Name (ARN) of the transit gateway.

    " + }, + "State":{ + "shape":"TransitGatewayRegistrationStateReason", + "documentation":"

    The state of the transit gateway registration.

    " + } + }, + "documentation":"

    Describes the registration of a transit gateway to a global network.

    " + }, + "TransitGatewayRegistrationList":{ + "type":"list", + "member":{"shape":"TransitGatewayRegistration"} + }, + "TransitGatewayRegistrationState":{ + "type":"string", + "enum":[ + "PENDING", + "AVAILABLE", + "DELETING", + "DELETED", + "FAILED" + ] + }, + "TransitGatewayRegistrationStateReason":{ + "type":"structure", + "members":{ + "Code":{ + "shape":"TransitGatewayRegistrationState", + "documentation":"

    The code for the state reason.

    " + }, + "Message":{ + "shape":"String", + "documentation":"

    The message for the state reason.

    " + } + }, + "documentation":"

    Describes the status of a transit gateway registration.

    " + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "TagKeys" + ], + "members":{ + "ResourceArn":{ + "shape":"ResourceARN", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    ", + "location":"uri", + "locationName":"resourceArn" + }, + "TagKeys":{ + "shape":"TagKeyList", + "documentation":"

    The tag keys to remove from the specified resource.

    ", + "location":"querystring", + "locationName":"tagKeys" + } + } + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "UpdateDeviceRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "DeviceId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "DeviceId":{ + "shape":"String", + "documentation":"

    The ID of the device.

    ", + "location":"uri", + "locationName":"deviceId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of the device.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Type":{ + "shape":"String", + "documentation":"

    The type of the device.

    " + }, + "Vendor":{ + "shape":"String", + "documentation":"

    The vendor of the device.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Model":{ + "shape":"String", + "documentation":"

    The model of the device.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "SerialNumber":{ + "shape":"String", + "documentation":"

    The serial number of the device.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Location":{"shape":"Location"}, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of the site.

    " + } + } + }, + "UpdateDeviceResponse":{ + "type":"structure", + "members":{ + "Device":{ + "shape":"Device", + "documentation":"

    Information about the device.

    " + } + } + }, + "UpdateGlobalNetworkRequest":{ + "type":"structure", + "required":["GlobalNetworkId"], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of your global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of the global network.

    Length Constraints: Maximum length of 256 characters.

    " + } + } + }, + "UpdateGlobalNetworkResponse":{ + "type":"structure", + "members":{ + "GlobalNetwork":{ + "shape":"GlobalNetwork", + "documentation":"

    Information about the global network object.

    " + } + } + }, + "UpdateLinkRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "LinkId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "LinkId":{ + "shape":"String", + "documentation":"

    The ID of the link.

    ", + "location":"uri", + "locationName":"linkId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of the link.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Type":{ + "shape":"String", + "documentation":"

    The type of the link.

    Length Constraints: Maximum length of 128 characters.

    " + }, + "Bandwidth":{ + "shape":"Bandwidth", + "documentation":"

    The upload and download speed in Mbps.

    " + }, + "Provider":{ + "shape":"String", + "documentation":"

    The provider of the link.

    Length Constraints: Maximum length of 128 characters.

    " + } + } + }, + "UpdateLinkResponse":{ + "type":"structure", + "members":{ + "Link":{ + "shape":"Link", + "documentation":"

    Information about the link.

    " + } + } + }, + "UpdateSiteRequest":{ + "type":"structure", + "required":[ + "GlobalNetworkId", + "SiteId" + ], + "members":{ + "GlobalNetworkId":{ + "shape":"String", + "documentation":"

    The ID of the global network.

    ", + "location":"uri", + "locationName":"globalNetworkId" + }, + "SiteId":{ + "shape":"String", + "documentation":"

    The ID of your site.

    ", + "location":"uri", + "locationName":"siteId" + }, + "Description":{ + "shape":"String", + "documentation":"

    A description of your site.

    Length Constraints: Maximum length of 256 characters.

    " + }, + "Location":{ + "shape":"Location", + "documentation":"

    The site location:

    • Address: The physical address of the site.

    • Latitude: The latitude of the site.

    • Longitude: The longitude of the site.

    " + } + } + }, + "UpdateSiteResponse":{ + "type":"structure", + "members":{ + "Site":{ + "shape":"Site", + "documentation":"

    Information about the site.

    " + } + } + }, + "ValidationException":{ + "type":"structure", + "required":["Message"], + "members":{ + "Message":{"shape":"String"}, + "Reason":{ + "shape":"ValidationExceptionReason", + "documentation":"

    The reason for the error.

    " + }, + "Fields":{ + "shape":"ValidationExceptionFieldList", + "documentation":"

    The fields that caused the error, if applicable.

    " + } + }, + "documentation":"

    The input fails to satisfy the constraints.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ValidationExceptionField":{ + "type":"structure", + "required":[ + "Name", + "Message" + ], + "members":{ + "Name":{ + "shape":"String", + "documentation":"

    The name of the field.

    " + }, + "Message":{ + "shape":"String", + "documentation":"

    The message for the field.

    " + } + }, + "documentation":"

    Describes a validation exception for a field.

    " + }, + "ValidationExceptionFieldList":{ + "type":"list", + "member":{"shape":"ValidationExceptionField"} + }, + "ValidationExceptionReason":{ + "type":"string", + "enum":[ + "UnknownOperation", + "CannotParse", + "FieldValidationFailed", + "Other" + ] + } + }, + "documentation":"

    Transit Gateway Network Manager (Network Manager) enables you to create a global network, in which you can monitor your AWS and on-premises networks that are built around transit gateways.

    " +} diff --git a/services/new-service-template/pom.xml b/services/new-service-template/pom.xml index 78d4d4349f88..2ae4673db1e7 100644 --- a/services/new-service-template/pom.xml +++ b/services/new-service-template/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + outposts + AWS Java SDK :: Services :: Outposts + The AWS Java SDK for Outposts module holds the client classes that are used for + communicating with Outposts. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.outposts + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/outposts/src/main/resources/codegen-resources/paginators-1.json b/services/outposts/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..a22e9d094c75 --- /dev/null +++ b/services/outposts/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,14 @@ +{ + "pagination": { + "ListOutposts": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, + "ListSites": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + } + } +} diff --git a/services/outposts/src/main/resources/codegen-resources/service-2.json b/services/outposts/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..592e20363153 --- /dev/null +++ b/services/outposts/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,467 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-12-03", + "endpointPrefix":"outposts", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"Outposts", + "serviceFullName":"AWS Outposts", + "serviceId":"Outposts", + "signatureVersion":"v4", + "signingName":"outposts", + "uid":"outposts-2019-12-03" + }, + "operations":{ + "CreateOutpost":{ + "name":"CreateOutpost", + "http":{ + "method":"POST", + "requestUri":"/outposts" + }, + "input":{"shape":"CreateOutpostInput"}, + "output":{"shape":"CreateOutpostOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"NotFoundException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceQuotaExceededException"} + ], + "documentation":"

    Creates an Outpost.

    " + }, + "DeleteOutpost":{ + "name":"DeleteOutpost", + "http":{ + "method":"DELETE", + "requestUri":"/outposts/{OutpostId}" + }, + "input":{"shape":"DeleteOutpostInput"}, + "output":{"shape":"DeleteOutpostOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"NotFoundException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes the Outpost.

    " + }, + "DeleteSite":{ + "name":"DeleteSite", + "http":{ + "method":"DELETE", + "requestUri":"/sites/{SiteId}" + }, + "input":{"shape":"DeleteSiteInput"}, + "output":{"shape":"DeleteSiteOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"NotFoundException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes the site.

    " + }, + "GetOutpost":{ + "name":"GetOutpost", + "http":{ + "method":"GET", + "requestUri":"/outposts/{OutpostId}" + }, + "input":{"shape":"GetOutpostInput"}, + "output":{"shape":"GetOutpostOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"NotFoundException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Gets information about the specified Outpost.

    " + }, + "GetOutpostInstanceTypes":{ + "name":"GetOutpostInstanceTypes", + "http":{ + "method":"GET", + "requestUri":"/outposts/{OutpostId}/instanceTypes" + }, + "input":{"shape":"GetOutpostInstanceTypesInput"}, + "output":{"shape":"GetOutpostInstanceTypesOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"NotFoundException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Lists the instance types for the specified Outpost.

    " + }, + "ListOutposts":{ + "name":"ListOutposts", + "http":{ + "method":"GET", + "requestUri":"/outposts" + }, + "input":{"shape":"ListOutpostsInput"}, + "output":{"shape":"ListOutpostsOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    List the Outposts for your AWS account.

    " + }, + "ListSites":{ + "name":"ListSites", + "http":{ + "method":"GET", + "requestUri":"/sites" + }, + "input":{"shape":"ListSitesInput"}, + "output":{"shape":"ListSitesOutput"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"AccessDeniedException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Lists the sites for the specified AWS account.

    " + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You do not have permission to perform this operation.

    ", + "error":{"httpStatusCode":403}, + "exception":true + }, + "AccountId":{ + "type":"string", + "documentation":"

    The ID of the AWS account.

    ", + "max":12, + "min":12 + }, + "AvailabilityZone":{ + "type":"string", + "documentation":"

    The Availability Zone.

    You must specify AvailabilityZone or AvailabilityZoneId.

    ", + "max":1000, + "min":1, + "pattern":"[a-z\\d-]+" + }, + "AvailabilityZoneId":{ + "type":"string", + "documentation":"

    The ID of the Availability Zone.

    You must specify AvailabilityZone or AvailabilityZoneId.

    ", + "max":255, + "min":1, + "pattern":"[a-z]+[0-9]+-az[0-9]+" + }, + "CreateOutpostInput":{ + "type":"structure", + "required":["SiteId"], + "members":{ + "Name":{"shape":"OutpostName"}, + "Description":{"shape":"OutpostDescription"}, + "SiteId":{"shape":"SiteId"}, + "AvailabilityZone":{"shape":"AvailabilityZone"}, + "AvailabilityZoneId":{"shape":"AvailabilityZoneId"} + } + }, + "CreateOutpostOutput":{ + "type":"structure", + "members":{ + "Outpost":{"shape":"Outpost"} + } + }, + "DeleteOutpostInput":{ + "type":"structure", + "required":["OutpostId"], + "members":{ + "OutpostId":{ + "shape":"OutpostId", + "location":"uri", + "locationName":"OutpostId" + } + } + }, + "DeleteOutpostOutput":{ + "type":"structure", + "members":{ + } + }, + "DeleteSiteInput":{ + "type":"structure", + "required":["SiteId"], + "members":{ + "SiteId":{ + "shape":"SiteId", + "location":"uri", + "locationName":"SiteId" + } + } + }, + "DeleteSiteOutput":{ + "type":"structure", + "members":{ + } + }, + "ErrorMessage":{ + "type":"string", + "max":1000, + "min":1, + "pattern":"^[\\S \\n]+$" + }, + "GetOutpostInput":{ + "type":"structure", + "required":["OutpostId"], + "members":{ + "OutpostId":{ + "shape":"OutpostId", + "location":"uri", + "locationName":"OutpostId" + } + } + }, + "GetOutpostInstanceTypesInput":{ + "type":"structure", + "required":["OutpostId"], + "members":{ + "OutpostId":{ + "shape":"OutpostId", + "location":"uri", + "locationName":"OutpostId" + }, + "NextToken":{ + "shape":"Token", + "location":"querystring", + "locationName":"NextToken" + }, + "MaxResults":{ + "shape":"MaxResults1000", + "location":"querystring", + "locationName":"MaxResults" + } + } + }, + "GetOutpostInstanceTypesOutput":{ + "type":"structure", + "members":{ + "InstanceTypes":{"shape":"InstanceTypeListDefinition"}, + "NextToken":{"shape":"Token"}, + "OutpostId":{"shape":"OutpostId"}, + "OutpostArn":{"shape":"OutpostArn"} + } + }, + "GetOutpostOutput":{ + "type":"structure", + "members":{ + "Outpost":{"shape":"Outpost"} + } + }, + "InstanceType":{ + "type":"string", + "documentation":"

    The instance type.

    " + }, + "InstanceTypeItem":{ + "type":"structure", + "members":{ + "InstanceType":{"shape":"InstanceType"} + }, + "documentation":"

    Information about an instance type.

    " + }, + "InstanceTypeListDefinition":{ + "type":"list", + "member":{"shape":"InstanceTypeItem"}, + "documentation":"

    Information about the instance types.

    " + }, + "InternalServerException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    An internal error has occurred.

    ", + "error":{"httpStatusCode":500}, + "exception":true + }, + "LifeCycleStatus":{ + "type":"string", + "documentation":"

    The life cycle status.

    " + }, + "ListOutpostsInput":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"Token", + "location":"querystring", + "locationName":"NextToken" + }, + "MaxResults":{ + "shape":"MaxResults1000", + "location":"querystring", + "locationName":"MaxResults" + } + } + }, + "ListOutpostsOutput":{ + "type":"structure", + "members":{ + "Outposts":{"shape":"outpostListDefinition"}, + "NextToken":{"shape":"Token"} + } + }, + "ListSitesInput":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"Token", + "location":"querystring", + "locationName":"NextToken" + }, + "MaxResults":{ + "shape":"MaxResults1000", + "location":"querystring", + "locationName":"MaxResults" + } + } + }, + "ListSitesOutput":{ + "type":"structure", + "members":{ + "Sites":{"shape":"siteListDefinition"}, + "NextToken":{"shape":"Token"} + } + }, + "MaxResults1000":{ + "type":"integer", + "documentation":"

    The maximum page size.

    ", + "box":true, + "max":1000, + "min":1 + }, + "NotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The specified request is not valid.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "Outpost":{ + "type":"structure", + "members":{ + "OutpostId":{"shape":"OutpostId"}, + "OwnerId":{"shape":"OwnerId"}, + "OutpostArn":{"shape":"OutpostArn"}, + "SiteId":{"shape":"SiteId"}, + "Name":{"shape":"OutpostName"}, + "Description":{"shape":"OutpostDescription"}, + "LifeCycleStatus":{"shape":"LifeCycleStatus"}, + "AvailabilityZone":{"shape":"AvailabilityZone"}, + "AvailabilityZoneId":{"shape":"AvailabilityZoneId"} + }, + "documentation":"

    Information about an Outpost.

    " + }, + "OutpostArn":{ + "type":"string", + "documentation":"

    The Amazon Resource Name (ARN) of the Outpost.

    ", + "max":255, + "min":1, + "pattern":"^arn:aws([a-z-]+)?:outposts:[a-z\\d-]+:\\d{12}:outpost/op-[a-f0-9]{17}$" + }, + "OutpostDescription":{ + "type":"string", + "documentation":"

    The Outpost description.

    ", + "max":1000, + "min":1, + "pattern":"^[\\S ]+$" + }, + "OutpostId":{ + "type":"string", + "documentation":"

    The ID of the Outpost.

    ", + "max":180, + "min":1, + "pattern":"^(arn:aws([a-z-]+)?:outposts:[a-z\\d-]+:\\d{12}:outpost/)?op-[a-f0-9]{17}$" + }, + "OutpostName":{ + "type":"string", + "documentation":"

    The name of the Outpost.

    ", + "max":255, + "min":1, + "pattern":"^[\\S ]+$" + }, + "OwnerId":{ + "type":"string", + "documentation":"

    The AWS account ID of the Outpost owner.

    ", + "max":12, + "min":12, + "pattern":"\\d{12}" + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    You have exceeded a service quota.

    ", + "error":{"httpStatusCode":402}, + "exception":true + }, + "Site":{ + "type":"structure", + "members":{ + "SiteId":{"shape":"SiteId"}, + "AccountId":{"shape":"AccountId"}, + "Name":{"shape":"SiteName"}, + "Description":{"shape":"SiteDescription"} + }, + "documentation":"

    Information about a site.

    " + }, + "SiteDescription":{ + "type":"string", + "documentation":"

    The description of the site.

    ", + "max":1000, + "min":1, + "pattern":"^[\\S ]+$" + }, + "SiteId":{ + "type":"string", + "documentation":"

    The ID of the site.

    ", + "max":255, + "min":1, + "pattern":"os-[a-f0-9]{17}" + }, + "SiteName":{ + "type":"string", + "documentation":"

    The name of the site.

    ", + "max":1000, + "min":1, + "pattern":"^[\\S ]+$" + }, + "Token":{ + "type":"string", + "documentation":"

    The pagination token.

    ", + "max":1005, + "min":1, + "pattern":".*\\S.*" + }, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    A parameter is not valid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "outpostListDefinition":{ + "type":"list", + "member":{"shape":"Outpost"}, + "documentation":"

    Information about the Outposts.

    " + }, + "siteListDefinition":{ + "type":"list", + "member":{"shape":"Site"}, + "documentation":"

    Information about the sites.

    " + } + }, + "documentation":"

    AWS Outposts is a fully-managed service that extends AWS infrastructure, APIs, and tools to customer premises. By providing local access to AWS-managed infrastructure, AWS Outposts enables customers to build and run applications on premises using the same programming interfaces as in AWS Regions, while using local compute and storage resources for lower latency and local data processing needs.

    " +} diff --git a/services/personalize/pom.xml b/services/personalize/pom.xml index 596fda553730..631531f95053 100644 --- a/services/personalize/pom.xml +++ b/services/personalize/pom.xml @@ -1,6 +1,6 @@ + 4.0.0 software.amazon.awssdk aws-sdk-java-pom - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT services AWS Java SDK :: Services @@ -198,6 +213,34 @@ qldb qldbsession workmailmessageflow + codestarnotifications + savingsplans + sso + ssooidc + marketplacecatalog + sesv2 + dataexchange + migrationhubconfig + connectparticipant + wafv2 + appconfig + iotsecuretunneling + elasticinference + imagebuilder + schemas + accessanalyzer + computeoptimizer + networkmanager + kendra + frauddetector + codegurureviewer + codeguruprofiler + outposts + sagemakera2iruntime + ebs + kinesisvideosignaling + detective + codestarconnections The AWS Java SDK services https://aws.amazon.com/sdkforjava diff --git a/services/pricing/pom.xml b/services/pricing/pom.xml index 2bca4a3a79f6..45e615f43b6c 100644 --- a/services/pricing/pom.xml +++ b/services/pricing/pom.xml @@ -1,6 +1,6 @@ commons-io @@ -80,10 +90,27 @@ ${awsjavasdk.version} test + + s3control + software.amazon.awssdk + ${awsjavasdk.version} + test + + + kms + software.amazon.awssdk + ${awsjavasdk.version} + test + org.apache.commons commons-lang3 test + + com.github.tomakehurst + wiremock + test + diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AccessPointsIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AccessPointsIntegrationTest.java new file mode 100644 index 000000000000..8dee1096fa22 --- /dev/null +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AccessPointsIntegrationTest.java @@ -0,0 +1,109 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; + +import java.util.StringJoiner; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3control.S3ControlClient; +import software.amazon.awssdk.services.sts.StsClient; + +public class AccessPointsIntegrationTest extends S3IntegrationTestBase { + + private static final String BUCKET = temporaryBucketName(AccessPointsIntegrationTest.class); + + private static final String AP_NAME = "java-sdk-" + System.currentTimeMillis(); + + private static final String KEY = "some-key"; + + private static S3ControlClient s3control; + + private static StsClient sts; + + private static String accountId; + + @BeforeClass + public static void setupFixture() { + createBucket(BUCKET); + + s3control = S3ControlClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) + .build(); + + sts = StsClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) + .build(); + + accountId = sts.getCallerIdentity().account(); + s3control.createAccessPoint(r -> r.accountId(accountId) + .bucket(BUCKET) + .name(AP_NAME)); + } + + @AfterClass + public static void tearDown() { + deleteBucketAndAllContents(BUCKET); + s3control.deleteAccessPoint(b -> b.accountId(accountId).name(AP_NAME)); + } + + @Test + public void transfer_Succeeds_UsingAccessPoint() { + StringJoiner apArn = new StringJoiner(":"); + apArn.add("arn").add("aws").add("s3").add("us-west-2").add(accountId).add("accesspoint").add(AP_NAME); + + s3.putObject(PutObjectRequest.builder() + .bucket(apArn.toString()) + .key(KEY) + .build(), RequestBody.fromString("helloworld")); + + String objectContent = s3.getObjectAsBytes(GetObjectRequest.builder() + .bucket(apArn.toString()) + .key(KEY) + .build()).asUtf8String(); + + assertThat(objectContent).isEqualTo("helloworld"); + } + + @Test + public void transfer_Succeeds_UsingAccessPoint_CrossRegion() { + S3Client s3DifferentRegion = + s3ClientBuilder().region(Region.US_EAST_1).serviceConfiguration(c -> c.useArnRegionEnabled(true)).build(); + + StringJoiner apArn = new StringJoiner(":"); + apArn.add("arn").add("aws").add("s3").add("us-west-2").add(accountId).add("accesspoint").add(AP_NAME); + + s3DifferentRegion.putObject(PutObjectRequest.builder() + .bucket(apArn.toString()) + .key(KEY) + .build(), RequestBody.fromString("helloworld")); + + String objectContent = s3DifferentRegion.getObjectAsBytes(GetObjectRequest.builder() + .bucket(apArn.toString()) + .key(KEY) + .build()).asUtf8String(); + + assertThat(objectContent).isEqualTo("helloworld"); + } +} diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AclIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AclIntegrationTest.java index f9170c5743d8..c5bb38836cc8 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AclIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AclIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,13 +16,11 @@ package software.amazon.awssdk.services.s3; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertNotNull; import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; -import java.io.File; -import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -30,9 +28,7 @@ import software.amazon.awssdk.services.s3.model.AccessControlPolicy; import software.amazon.awssdk.services.s3.model.GetBucketAclResponse; import software.amazon.awssdk.services.s3.model.GetObjectAclResponse; -import software.amazon.awssdk.services.s3.model.Grant; import software.amazon.awssdk.services.s3.model.PutObjectRequest; -import software.amazon.awssdk.services.s3.model.Type; public class AclIntegrationTest extends S3IntegrationTestBase { @@ -55,10 +51,12 @@ public static void deleteAllBuckets() { } @Test - public void putObjectAcl() { + public void putGetObjectAcl() { GetObjectAclResponse objectAcl = s3.getObjectAcl(b -> b.bucket(BUCKET).key(KEY)); + GetObjectAclResponse objectAclAsyncResponse = s3Async.getObjectAcl(b -> b.bucket(BUCKET).key(KEY)).join(); + assertThat(objectAcl.equalsBySdkFields(objectAclAsyncResponse)).isTrue(); Consumer aclBuilder = a -> a.owner(objectAcl.owner()) - .grants(addGranteeType(objectAcl.grants())); + .grants(objectAcl.grants()); assertNotNull(s3.putObjectAcl(b -> b.bucket(BUCKET) @@ -71,24 +69,16 @@ public void putObjectAcl() { } @Test - public void putBucketAcl() { + public void putGetBucketAcl() { GetBucketAclResponse bucketAcl = s3.getBucketAcl(b -> b.bucket(BUCKET)); + GetBucketAclResponse bucketAclAsyncResponse = s3Async.getBucketAcl(b -> b.bucket(BUCKET)).join(); + assertThat(bucketAcl.equalsBySdkFields(bucketAclAsyncResponse)).isTrue(); Consumer aclBuilder = a -> a.owner(bucketAcl.owner()) - .grants(addGranteeType(bucketAcl.grants())); + .grants(bucketAcl.grants()); assertNotNull(s3.putBucketAcl(b -> b.bucket(BUCKET) .accessControlPolicy(aclBuilder))); assertNotNull(s3Async.putBucketAcl(b -> b.bucket(BUCKET) .accessControlPolicy(aclBuilder)).join()); } - - //TODO: remove this once we fix the unmarshalling issue for Grant#type - private List addGranteeType(List grants) { - return grants.stream().map(g -> g.toBuilder().grantee(g.grantee() - .toBuilder() - .type(Type.CANONICAL_USER) - .build()).build()) - .collect(Collectors.toList()); - } - } diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncGetObjectFaultIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncGetObjectFaultIntegrationTest.java index 207dcedd5293..6b3972c2f190 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncGetObjectFaultIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncGetObjectFaultIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncResponseTransformerIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncResponseTransformerIntegrationTest.java index 58fde0a96955..945ac902af8e 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncResponseTransformerIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncResponseTransformerIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncServerSideEncryptionIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncServerSideEncryptionIntegrationTest.java index 43d59c593ad8..70241622f610 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncServerSideEncryptionIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncServerSideEncryptionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -14,47 +14,21 @@ */ package software.amazon.awssdk.services.s3; -import static org.assertj.core.api.Fail.fail; import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AES256; -import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.IOException; -import java.security.SecureRandom; import java.util.Base64; import java.util.UUID; -import javax.crypto.KeyGenerator; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import software.amazon.awssdk.core.async.AsyncResponseTransformer; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.ServerSideEncryption; -import software.amazon.awssdk.testutils.RandomTempFile; import software.amazon.awssdk.testutils.SdkAsserts; import software.amazon.awssdk.utils.Md5Utils; -public class AsyncServerSideEncryptionIntegrationTest extends S3IntegrationTestBase { - - private static final String BUCKET = temporaryBucketName(GetObjectIntegrationTest.class); - - private static File file; - - @BeforeClass - public static void setupFixture() throws IOException { - createBucket(BUCKET); - file = new RandomTempFile(10_000); - } - - @AfterClass - public static void tearDownFixture() { - deleteBucketAndAllContents(BUCKET); - file.delete(); - } - +public class AsyncServerSideEncryptionIntegrationTest extends ServerSideEncryptionIntegrationTestBase { @Test public void sse_AES256_succeeds() throws FileNotFoundException { String key = UUID.randomUUID().toString(); @@ -121,20 +95,27 @@ public void sse_customerManaged_succeeds() throws FileNotFoundException { verifyGetResponse(getObjectRequest); } + @Test + public void sse_onBucket_succeeds() throws FileNotFoundException { + String key = UUID.randomUUID().toString(); + + PutObjectRequest request = PutObjectRequest.builder() + .key(key) + .bucket(BUCKET_WITH_SSE) + .build(); + + s3Async.putObject(request, file.toPath()).join(); + + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .key(key) + .bucket(BUCKET_WITH_SSE) + .build(); + + verifyGetResponse(getObjectRequest); + } + private void verifyGetResponse(GetObjectRequest getObjectRequest) throws FileNotFoundException { String response = s3Async.getObject(getObjectRequest, AsyncResponseTransformer.toBytes()).join().asUtf8String(); SdkAsserts.assertStringEqualsStream(response, new FileInputStream(file)); } - - private static byte[] generateSecretKey() { - KeyGenerator generator; - try { - generator = KeyGenerator.getInstance("AES"); - generator.init(256, new SecureRandom()); - return generator.generateKey().getEncoded(); - } catch (Exception e) { - fail("Unable to generate symmetric key: " + e.getMessage()); - return null; - } - } } diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncUploadMultiplePartIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncUploadMultiplePartIntegrationTest.java index 36e942dad25e..09445bc16ee9 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncUploadMultiplePartIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/AsyncUploadMultiplePartIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAccelerateIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAccelerateIntegrationTest.java index 79cf1b963a72..cee5a393032a 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAccelerateIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAccelerateIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAnalyticsConfigurationIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAnalyticsConfigurationIntegrationTest.java index 1b155687aa2b..c9062ccadd55 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAnalyticsConfigurationIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketAnalyticsConfigurationIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketInventoryConfigurationIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketInventoryConfigurationIntegrationTest.java index ab2298268e79..9a866d8b5768 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketInventoryConfigurationIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/BucketInventoryConfigurationIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/CopyObjectIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/CopyObjectIntegrationTest.java index 3f73265db1ba..2e956d06538d 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/CopyObjectIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/CopyObjectIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/CreateBucketIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/CreateBucketIntegrationTest.java index 902f2589b7c5..f83d504bb2ef 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/CreateBucketIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/CreateBucketIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -21,8 +21,8 @@ import org.junit.Test; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.model.CreateBucketRequest; -import software.amazon.awssdk.services.s3.model.GetBucketLocationRequest; import software.amazon.awssdk.services.s3.utils.S3TestUtils; +import software.amazon.awssdk.testutils.Waiter; public class CreateBucketIntegrationTest extends S3IntegrationTestBase { @@ -44,9 +44,8 @@ public static void cleanup() { public void createBucket_InUsEast1_Succeeds() { US_EAST_1_CLIENT.createBucket(CreateBucketRequest.builder().bucket(US_EAST_1_BUCKET_NAME).build()); - String region = US_EAST_1_CLIENT.getBucketLocation(GetBucketLocationRequest.builder() - .bucket(US_EAST_1_BUCKET_NAME) - .build()) + String region = Waiter.run(() -> US_EAST_1_CLIENT.getBucketLocation(r -> r.bucket(US_EAST_1_BUCKET_NAME))) + .orFail() .locationConstraintAsString(); assertThat(region).isEqualToIgnoringCase(""); } @@ -56,7 +55,9 @@ public void createBucket_Succeeds_WithoutSpecifyingBucketLocation() { S3Client client = S3Client.builder().region(Region.US_WEST_2).credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); client.createBucket(CreateBucketRequest.builder().bucket(BUCKET_NAME).build()); - String region = client.getBucketLocation(GetBucketLocationRequest.builder().bucket(BUCKET_NAME).build()).locationConstraintAsString(); + String region = Waiter.run(() -> client.getBucketLocation(r -> r.bucket(BUCKET_NAME))) + .orFail() + .locationConstraintAsString(); assertThat(region).isEqualToIgnoringCase("us-west-2"); } diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ExceptionUnmarshallingIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ExceptionUnmarshallingIntegrationTest.java index cd2c158e8d30..f16a21ff5039 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ExceptionUnmarshallingIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ExceptionUnmarshallingIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -19,8 +19,11 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; +import java.nio.charset.StandardCharsets; + import org.junit.AfterClass; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.model.BucketAlreadyExistsException; @@ -163,6 +166,17 @@ public void asyncHeadBucketWrongRegion() { .satisfies(e -> assertThat(((S3Exception) (e.getCause())).statusCode()).isEqualTo(301)); } + @Test + @Ignore("TODO") + public void errorResponseContainsRawBytes() { + assertThatThrownBy(() -> s3.getObjectAcl(b -> b.bucket(BUCKET + KEY).key(KEY))) + .isInstanceOf(NoSuchBucketException.class) + .satisfies(e -> assertThat( + ((NoSuchBucketException) e).awsErrorDetails().rawResponse().asString(StandardCharsets.UTF_8)) + .startsWith("\nNoSuchBucketThe " + + "specified bucket does not exist")); + } + private void assertMetadata(S3Exception e, String expectedErrorCode) { assertThat(e.awsErrorDetails()).satisfies( errorDetails -> { diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectAsyncIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectAsyncIntegrationTest.java index 356ae78fa893..eb8a07a08aae 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectAsyncIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectAsyncIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectFaultIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectFaultIntegrationTest.java index 501b896f57bb..414e8edb4727 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectFaultIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectFaultIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectIntegrationTest.java index 2c92e61746d4..80a2665ce519 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/GetObjectIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/KeysWithLeadingSlashIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/KeysWithLeadingSlashIntegrationTest.java index dcc8f0b25454..db3d6dcf66d5 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/KeysWithLeadingSlashIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/KeysWithLeadingSlashIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsIntegrationTest.java new file mode 100644 index 000000000000..d3a039f7478e --- /dev/null +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsIntegrationTest.java @@ -0,0 +1,193 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.model.EncodingType; +import software.amazon.awssdk.services.s3.model.ListObjectsRequest; +import software.amazon.awssdk.services.s3.model.ListObjectsResponse; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.S3Object; + +/** + * Integration tests for the listObjects operation in the Amazon S3 Java + * client. + */ +public class ListObjectsIntegrationTest extends S3IntegrationTestBase { + /** + * One hour in milliseconds for verifying that a last modified date is recent. + */ + private static final long ONE_HOUR_IN_MILLISECONDS = 1000 * 60 * 60; + + private static final String KEY_NAME_WITH_SPECIAL_CHARS = "special-chars-@$%"; + private static final int BUCKET_OBJECTS = 15; + /** + * The name of the bucket created, used, and deleted by these tests. + */ + private static String bucketName = temporaryBucketName("list-objects-integ-test"); + /** + * List of all keys created by these tests. + */ + private static List keys = new ArrayList<>(); + + + /** + * Releases all resources created in this test. + */ + @AfterClass + public static void tearDown() { + deleteBucketAndAllContents(bucketName); + } + + /** + * Creates all the test resources for the tests. + */ + @BeforeClass + public static void createResources() throws Exception { + createBucket(bucketName); + + NumberFormat numberFormatter = new DecimalFormat("##00"); + for (int i = 1; i <= BUCKET_OBJECTS; i++) { + createKey("key-" + numberFormatter.format(i)); + } + createKey("aaaaa"); + createKey("aaaaa/aaaaa/aaaaa"); + createKey("aaaaa/aaaaa+a"); + createKey("aaaaa/aaaaa//aaaaa"); + createKey(KEY_NAME_WITH_SPECIAL_CHARS); + } + + private static void createKey(String key) { + s3.putObject(PutObjectRequest.builder() + .bucket(bucketName) + .key(key) + .build(), + RequestBody.fromString(RandomStringUtils.random(1000))); + keys.add(key); + } + + @Test + public void listObjectsNoParameters() { + ListObjectsResponse result = s3.listObjects(ListObjectsRequest.builder().bucket(bucketName).build()); + List objects = result.contents(); + + assertEquals(keys.size(), objects.size()); + assertEquals(bucketName, result.name()); + assertS3ObjectSummariesAreValid(objects); + assertNotNull(result.maxKeys()); + + // We didn't use a delimiter, so we expect these to be empty/null + assertNull(result.delimiter()); + + // We don't expect any truncated results + assertFalse(result.isTruncated()); + + // We didn't set other request parameters, so we expect them to be empty + assertNull(result.encodingType()); + assertThat(result.prefix()).isEmpty(); + } + + @Test + public void listObjectsWithAllElements() { + String delimiter = "/"; + String marker = "aaa"; + ListObjectsResponse result = s3.listObjects(ListObjectsRequest.builder() + .bucket(bucketName) + .prefix(KEY_NAME_WITH_SPECIAL_CHARS) + .marker(marker) + .encodingType(EncodingType.URL) + .delimiter(delimiter) + .build()); + List objects = result.contents(); + + assertEquals(bucketName, result.name()); + assertS3ObjectSummariesAreValid(objects); + assertEquals(marker, result.marker()); + assertEquals(delimiter, result.delimiter()); + assertEquals(KEY_NAME_WITH_SPECIAL_CHARS, result.prefix()); + + assertFalse(result.isTruncated()); + assertTrue(result.maxKeys() >= 1000); + } + + @Test + public void listObjectsWithMaxKeys() { + int maxKeys = 4; + ListObjectsResponse result = s3.listObjects(ListObjectsRequest.builder() + .bucket(bucketName) + .maxKeys(maxKeys) + .build()); + + List objects = result.contents(); + + assertEquals(maxKeys, objects.size()); + assertEquals(bucketName, result.name()); + assertThat(maxKeys).isEqualTo(result.maxKeys()); + assertS3ObjectSummariesAreValid(objects); + + // We didn't use a delimiter, so we expect this to be empty/null + assertNull(result.delimiter()); + + // We expect truncated results since we set maxKeys + assertTrue(result.isTruncated()); + + // URL encoding is requested by default + + // We didn't set other request parameters, so we expect them to be empty + assertNull(result.encodingType()); + assertThat(result.prefix()).isEmpty(); + assertNull(result.delimiter()); + } + + /** + * Asserts that a list of S3Object objects are valid, by checking + * that expected fields are not null or empty, that ETag values don't + * contain leading or trailing quotes, that the last modified date is + * recent, etc. + * @param objectSummaries The list of objects to validate. + * + */ + private void assertS3ObjectSummariesAreValid(List objectSummaries) { + for (S3Object obj : objectSummaries) { + assertTrue(obj.eTag().length() > 1); + assertTrue(obj.key().length() > 1); + + // Verify that the last modified date is within an hour + assertNotNull(obj.lastModified()); + long offset = obj.lastModified().toEpochMilli() - Instant.now().toEpochMilli(); + assertTrue(offset < ONE_HOUR_IN_MILLISECONDS); + + assertTrue(obj.storageClassAsString().length() > 1); + } + } +} diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsV2PaginatorsIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsV2PaginatorsIntegrationTest.java index 79229e316977..f0333b3b715e 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsV2PaginatorsIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ListObjectsV2PaginatorsIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ObjectTaggingIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ObjectTaggingIntegrationTest.java index 4a5f488c4aea..f27a5534e889 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ObjectTaggingIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ObjectTaggingIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,11 +20,13 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.RandomStringUtils; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.model.BucketVersioningStatus; +import software.amazon.awssdk.services.s3.model.CompletedMultipartUpload; import software.amazon.awssdk.services.s3.model.CopyObjectRequest; import software.amazon.awssdk.services.s3.model.DeleteObjectTaggingRequest; import software.amazon.awssdk.services.s3.model.GetObjectTaggingRequest; @@ -33,6 +35,7 @@ import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.Tag; import software.amazon.awssdk.services.s3.model.Tagging; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; import software.amazon.awssdk.services.s3.model.VersioningConfiguration; /** @@ -95,10 +98,7 @@ public void putObject_WithTagging_Succeeds() { @Test public void getObjectTagging_Succeeds() { - List tagSet = new ArrayList<>(); - tagSet.add(Tag.builder().key("foo").value("1").build()); - tagSet.add(Tag.builder().key("bar").value("2").build()); - tagSet.add(Tag.builder().key("baz").value("3").build()); + List tagSet = tags(); Tagging tags = Tagging.builder().tagSet(tagSet).build(); @@ -147,10 +147,7 @@ public void putObjectTagging_Succeeds_WithUrlEncodedTags() { @Test public void copyObject_Succeeds_WithNewTags() { - List tagSet = new ArrayList<>(); - tagSet.add(Tag.builder().key("foo").value("1").build()); - tagSet.add(Tag.builder().key("bar").value("2").build()); - tagSet.add(Tag.builder().key("baz").value("3").build()); + List tagSet = tags(); Tagging tags = Tagging.builder().tagSet(tagSet).build(); @@ -183,12 +180,43 @@ public void copyObject_Succeeds_WithNewTags() { assertThat(getTaggingResult).containsExactlyInAnyOrder(tagsCopy.tagSet().toArray(new Tag[tagsCopy.tagSet().size()])); } - @Test - public void testDeleteObjectTagging() { + private List tags() { List tagSet = new ArrayList<>(); tagSet.add(Tag.builder().key("foo").value("1").build()); tagSet.add(Tag.builder().key("bar").value("2").build()); tagSet.add(Tag.builder().key("baz").value("3").build()); + return tagSet; + } + + @Test + public void multipartUploadWithNewTags_shouldSucceed() { + List tagSet = tags(); + + Tagging tags = Tagging.builder().tagSet(tagSet).build(); + + String key = makeNewKey(); + String uploadId = + s3.createMultipartUpload(b -> b.tagging(tags).bucket(BUCKET).key(key)).uploadId(); + + UploadPartResponse uploadPartResponse = s3.uploadPart(b -> b.bucket(BUCKET).key(key).partNumber(1).uploadId(uploadId), + RequestBody.fromString(RandomStringUtils.random(1000))); + CompletedMultipartUpload parts = + CompletedMultipartUpload.builder().parts(p -> p.partNumber(1).eTag(uploadPartResponse.eTag()).build()).build(); + + s3.completeMultipartUpload(b -> b.bucket(BUCKET).key(key).multipartUpload(parts).uploadId(uploadId).build()); + + List getTaggingResult = s3.getObjectTagging(GetObjectTaggingRequest.builder() + .bucket(BUCKET) + .key(key) + .build()) + .tagSet(); + + assertThat(getTaggingResult).containsExactlyInAnyOrder(tags.tagSet().toArray(new Tag[0])); + } + + @Test + public void testDeleteObjectTagging() { + List tagSet = tags(); Tagging tags = Tagging.builder().tagSet(tagSet).build(); diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/OperationsWithNonStandardResponsesIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/OperationsWithNonStandardResponsesIntegrationTest.java index aebc668c6b8f..bb2142bebbf6 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/OperationsWithNonStandardResponsesIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/OperationsWithNonStandardResponsesIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/PutObjectIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/PutObjectIntegrationTest.java index 77aeae12a5e0..4e548feb4bae 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/PutObjectIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/PutObjectIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3IntegrationTestBase.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3IntegrationTestBase.java index cc2700609c53..f38a93e59fdb 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3IntegrationTestBase.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3IntegrationTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ListObjectsV2IntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ListObjectsV2IntegrationTest.java index 761dd0a201e5..0e64f5e4135f 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ListObjectsV2IntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ListObjectsV2IntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3PresignerIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3PresignerIntegrationTest.java index 4aee95ce6293..735f3455b8af 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3PresignerIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3PresignerIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,33 +17,56 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.time.Duration; +import java.util.Optional; import java.util.UUID; +import java.util.function.Consumer; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; + +import software.amazon.awssdk.awscore.presigner.PresignedRequest; +import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.ContentStreamProvider; import software.amazon.awssdk.http.HttpExecuteRequest; import software.amazon.awssdk.http.HttpExecuteResponse; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.apache.ApacheHttpClient; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.ListMultipartUploadsResponse; +import software.amazon.awssdk.services.s3.model.MultipartUpload; import software.amazon.awssdk.services.s3.model.RequestPayer; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest; import software.amazon.awssdk.services.s3.utils.S3TestUtils; import software.amazon.awssdk.utils.IoUtils; +import software.amazon.awssdk.utils.StringInputStream; public class S3PresignerIntegrationTest { private static S3Client client; private static String testBucket; private static String testNonDnsCompatibleBucket; - private static String testObjectKey; + private static String testGetObjectKey; private static String testObjectContent; private S3Presigner presigner; @@ -53,11 +76,11 @@ public static void setUpClass() { client = S3Client.create(); testBucket = S3TestUtils.getTestBucket(client); testNonDnsCompatibleBucket = S3TestUtils.getNonDnsCompatibleTestBucket(client); - testObjectKey = "s3-presigner-it-" + UUID.randomUUID(); + testGetObjectKey = generateRandomObjectKey(); testObjectContent = "Howdy!"; - S3TestUtils.putObject(S3PresignerIntegrationTest.class, client, testBucket, testObjectKey, testObjectContent); - S3TestUtils.putObject(S3PresignerIntegrationTest.class, client, testNonDnsCompatibleBucket, testObjectKey, testObjectContent); + S3TestUtils.putObject(S3PresignerIntegrationTest.class, client, testBucket, testGetObjectKey, testObjectContent); + S3TestUtils.putObject(S3PresignerIntegrationTest.class, client, testNonDnsCompatibleBucket, testGetObjectKey, testObjectContent); } @AfterClass @@ -66,6 +89,10 @@ public static void tearDownClass() { client.close(); } + private static String generateRandomObjectKey() { + return "s3-presigner-it-" + UUID.randomUUID(); + } + @Before public void setUpInstance() { this.presigner = S3Presigner.create(); @@ -76,19 +103,20 @@ public void testDownInstance() { this.presigner.close(); } + @Test public void browserCompatiblePresignedUrlWorks() throws IOException { - assertThatPresigningWorks(testBucket, testObjectKey); + assertThatPresigningWorks(testBucket, testGetObjectKey); } @Test public void bucketsWithScaryCharactersWorks() throws IOException { - assertThatPresigningWorks(testNonDnsCompatibleBucket, testObjectKey); + assertThatPresigningWorks(testNonDnsCompatibleBucket, testGetObjectKey); } @Test public void keysWithScaryCharactersWorks() throws IOException { - String scaryObjectKey = testObjectKey + " !'/()~`"; + String scaryObjectKey = testGetObjectKey + " !'/()~`"; S3TestUtils.putObject(S3PresignerIntegrationTest.class, client, testBucket, scaryObjectKey, testObjectContent); assertThatPresigningWorks(testBucket, scaryObjectKey); @@ -111,7 +139,7 @@ public void browserIncompatiblePresignedUrlDoesNotWorkWithoutAdditionalHeaders() PresignedGetObjectRequest presigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) .getObjectRequest(gor -> gor.bucket(testBucket) - .key(testObjectKey) + .key(testGetObjectKey) .requestPayer(RequestPayer.REQUESTER))); assertThat(presigned.isBrowserExecutable()).isFalse(); @@ -130,7 +158,7 @@ public void browserIncompatiblePresignedUrlWorksWithAdditionalHeaders() throws I PresignedGetObjectRequest presigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) .getObjectRequest(gor -> gor.bucket(testBucket) - .key(testObjectKey) + .key(testGetObjectKey) .requestPayer(RequestPayer.REQUESTER))); assertThat(presigned.isBrowserExecutable()).isFalse(); @@ -149,11 +177,11 @@ public void browserIncompatiblePresignedUrlWorksWithAdditionalHeaders() throws I } @Test - public void presignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOException { + public void getObject_PresignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOException { PresignedGetObjectRequest presigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) .getObjectRequest(gor -> gor.bucket(testBucket) - .key(testObjectKey) + .key(testGetObjectKey) .requestPayer(RequestPayer.REQUESTER))); assertThat(presigned.isBrowserExecutable()).isFalse(); @@ -176,4 +204,171 @@ public void presignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOException { assertThat(IoUtils.toUtf8String(responseStream)).isEqualTo(testObjectContent); } } + + @Test + public void putObject_PresignedHttpRequestCanBeInvokedDirectlyBySdk() throws IOException { + String objectKey = generateRandomObjectKey(); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.deleteObject(r -> r.bucket(testBucket).key(objectKey))); + + PresignedPutObjectRequest presigned = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(por -> por.bucket(testBucket).key(objectKey))); + + assertThat(presigned.isBrowserExecutable()).isFalse(); + + SdkHttpClient httpClient = ApacheHttpClient.builder().build(); // or UrlConnectionHttpClient.builder().build() + + ContentStreamProvider requestPayload = () -> new StringInputStream(testObjectContent); + + HttpExecuteRequest request = HttpExecuteRequest.builder() + .request(presigned.httpRequest()) + .contentStreamProvider(requestPayload) + .build(); + + HttpExecuteResponse response = httpClient.prepareRequest(request).call(); + + assertThat(response.responseBody()).isPresent(); + assertThat(response.httpResponse().isSuccessful()).isTrue(); + response.responseBody().ifPresent(AbortableInputStream::abort); + String content = client.getObjectAsBytes(r -> r.bucket(testBucket).key(objectKey)).asUtf8String(); + assertThat(content).isEqualTo(testObjectContent); + } + + @Test + public void createMultipartUpload_CanBePresigned() throws IOException { + String objectKey = generateRandomObjectKey(); + + PresignedCreateMultipartUploadRequest presigned = + presigner.presignCreateMultipartUpload(p -> p.signatureDuration(Duration.ofMinutes(10)) + .createMultipartUploadRequest(createMultipartUploadRequest(objectKey))); + + HttpExecuteResponse response = execute(presigned, null); + + assertThat(response.httpResponse().isSuccessful()).isTrue(); + + Optional upload = getMultipartUpload(objectKey); + assertThat(upload).isPresent(); + + client.abortMultipartUpload(abortMultipartUploadRequest(objectKey, upload.get().uploadId())); + } + + @Test + public void uploadPart_CanBePresigned() throws IOException { + String objectKey = generateRandomObjectKey(); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.deleteObject(r -> r.bucket(testBucket).key(objectKey))); + + CreateMultipartUploadResponse create = client.createMultipartUpload(createMultipartUploadRequest(objectKey)); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.abortMultipartUpload(abortMultipartUploadRequest(objectKey, create.uploadId()))); + + PresignedUploadPartRequest uploadPart = + presigner.presignUploadPart(up -> up.signatureDuration(Duration.ofDays(1)) + .uploadPartRequest(upr -> upr.bucket(testBucket) + .key(objectKey) + .partNumber(1) + .uploadId(create.uploadId()))); + + + HttpExecuteResponse uploadPartResponse = execute(uploadPart, testObjectContent); + assertThat(uploadPartResponse.httpResponse().isSuccessful()).isTrue(); + String etag = uploadPartResponse.httpResponse().firstMatchingHeader("ETag").orElse(null); + + client.completeMultipartUpload(createMultipartUploadRequest(objectKey, create, etag)); + + String content = client.getObjectAsBytes(r -> r.bucket(testBucket).key(objectKey)).asUtf8String(); + assertThat(content).isEqualTo(testObjectContent); + } + + @Test + public void completeMultipartUpload_CanBePresigned() throws IOException { + String objectKey = generateRandomObjectKey(); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.deleteObject(r -> r.bucket(testBucket).key(objectKey))); + + CreateMultipartUploadResponse create = client.createMultipartUpload(createMultipartUploadRequest(objectKey)); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.abortMultipartUpload(abortMultipartUploadRequest(objectKey, create.uploadId()))); + + UploadPartResponse uploadPartResponse = client.uploadPart(uploadPartRequest(objectKey, create), + RequestBody.fromString(testObjectContent)); + String etag = uploadPartResponse.eTag(); + + PresignedCompleteMultipartUploadRequest presignedRequest = + presigner.presignCompleteMultipartUpload( + r -> r.signatureDuration(Duration.ofDays(1)) + .completeMultipartUploadRequest(createMultipartUploadRequest(objectKey, create, etag))); + + assertThat(execute(presignedRequest, presignedRequest.signedPayload().get().asUtf8String()) + .httpResponse().isSuccessful()).isTrue(); + + String content = client.getObjectAsBytes(r -> r.bucket(testBucket).key(objectKey)).asUtf8String(); + assertThat(content).isEqualTo(testObjectContent); + } + + @Test + public void abortMultipartUpload_CanBePresigned() throws IOException { + String objectKey = generateRandomObjectKey(); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.deleteObject(r -> r.bucket(testBucket).key(objectKey))); + + CreateMultipartUploadResponse create = client.createMultipartUpload(createMultipartUploadRequest(objectKey)); + S3TestUtils.addCleanupTask(S3PresignerIntegrationTest.class, + () -> client.abortMultipartUpload(abortMultipartUploadRequest(objectKey, create.uploadId()))); + + PresignedAbortMultipartUploadRequest presignedRequest = presigner.presignAbortMultipartUpload( + r -> r.signatureDuration(Duration.ofDays(1)) + .abortMultipartUploadRequest(abortMultipartUploadRequest(objectKey, create.uploadId()))); + + + assertThat(execute(presignedRequest, null).httpResponse().isSuccessful()).isTrue(); + + assertThat(getMultipartUpload(objectKey)).isNotPresent(); + } + + private Consumer createMultipartUploadRequest(String objectKey) { + return r -> r.bucket(testBucket).key(objectKey); + } + + private Consumer uploadPartRequest(String objectKey, CreateMultipartUploadResponse create) { + return r -> r.bucket(testBucket) + .key(objectKey) + .partNumber(1) + .uploadId(create.uploadId()); + } + + private Consumer createMultipartUploadRequest(String objectKey, CreateMultipartUploadResponse create, String etag) { + return c -> c.bucket(testBucket) + .key(objectKey) + .uploadId(create.uploadId()) + .multipartUpload(m -> m.parts(p -> p.partNumber(1).eTag(etag))); + } + + private Consumer abortMultipartUploadRequest(String objectKey, String uploadId) { + return r -> r.bucket(testBucket) + .key(objectKey) + .uploadId(uploadId); + } + + private Optional getMultipartUpload(String objectKey) { + return client.listMultipartUploadsPaginator(r -> r.bucket(testBucket).prefix(objectKey)) + .uploads() + .stream() + .filter(u -> u.key().equals(objectKey)) + .findAny(); + } + + private HttpExecuteResponse execute(PresignedRequest presigned, String payload) throws IOException { + SdkHttpClient httpClient = ApacheHttpClient.builder().build(); + + ContentStreamProvider requestPayload = payload == null ? null : () -> new StringInputStream(payload); + + HttpExecuteRequest request = HttpExecuteRequest.builder() + .request(presigned.httpRequest()) + .contentStreamProvider(requestPayload) + .build(); + + return httpClient.prepareRequest(request).call(); + } } diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ResponseMetadataIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ResponseMetadataIntegrationTest.java index 484f613a25d4..1763788d1627 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ResponseMetadataIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/S3ResponseMetadataIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/ServerSideEncryptionIntegrationTestBase.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ServerSideEncryptionIntegrationTestBase.java new file mode 100644 index 000000000000..32d7ba225af4 --- /dev/null +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/ServerSideEncryptionIntegrationTestBase.java @@ -0,0 +1,79 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3; + +import static org.assertj.core.api.Fail.fail; +import static software.amazon.awssdk.services.s3.S3IntegrationTestBase.createBucket; +import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; + +import java.io.File; +import java.io.IOException; +import java.security.SecureRandom; +import javax.crypto.KeyGenerator; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import software.amazon.awssdk.services.kms.KmsClient; +import software.amazon.awssdk.services.s3.model.ServerSideEncryption; +import software.amazon.awssdk.testutils.RandomTempFile; + +public class ServerSideEncryptionIntegrationTestBase extends S3IntegrationTestBase { + + protected static final String BUCKET = temporaryBucketName(ServerSideEncryptionIntegrationTestBase.class); + protected static final String BUCKET_WITH_SSE = temporaryBucketName(ServerSideEncryptionIntegrationTestBase.class); + + private static final KmsClient KMS = KmsClient.builder() + .region(DEFAULT_REGION) + .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) + .build(); + + protected static File file; + + private static String keyId; + + @BeforeClass + public static void setupFixture() throws IOException { + createBucket(BUCKET); + createBucket(BUCKET_WITH_SSE); + keyId = KMS.createKey().keyMetadata().keyId(); + + s3.putBucketEncryption(r -> r + .bucket(BUCKET_WITH_SSE) + .serverSideEncryptionConfiguration(ssec -> ssec + .rules(rule -> rule + .applyServerSideEncryptionByDefault(d -> d.kmsMasterKeyID(keyId) + .sseAlgorithm(ServerSideEncryption.AWS_KMS))))); + file = new RandomTempFile(10_000); + } + + @AfterClass + public static void tearDownFixture() { + deleteBucketAndAllContents(BUCKET); + deleteBucketAndAllContents(BUCKET_WITH_SSE); + file.delete(); + KMS.scheduleKeyDeletion(r -> r.keyId(keyId)); + } + + protected static byte[] generateSecretKey() { + KeyGenerator generator; + try { + generator = KeyGenerator.getInstance("AES"); + generator.init(256, new SecureRandom()); + return generator.generateKey().getEncoded(); + } catch (Exception e) { + fail("Unable to generate symmetric key: " + e.getMessage()); + return null; + } + } +} diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/SyncServerSideEncryptionIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/SyncServerSideEncryptionIntegrationTest.java index 3d7dc3901c40..23235b89a21b 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/SyncServerSideEncryptionIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/SyncServerSideEncryptionIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -14,47 +14,22 @@ */ package software.amazon.awssdk.services.s3; -import static org.assertj.core.api.Fail.fail; import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AES256; -import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; -import java.io.File; import java.io.FileInputStream; -import java.io.IOException; +import java.io.FileNotFoundException; import java.io.InputStream; -import java.security.SecureRandom; import java.util.Base64; import java.util.UUID; -import javax.crypto.KeyGenerator; -import org.junit.AfterClass; -import org.junit.BeforeClass; import org.junit.Test; import software.amazon.awssdk.core.sync.ResponseTransformer; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.ServerSideEncryption; -import software.amazon.awssdk.testutils.RandomTempFile; import software.amazon.awssdk.testutils.SdkAsserts; import software.amazon.awssdk.utils.Md5Utils; -public class SyncServerSideEncryptionIntegrationTest extends S3IntegrationTestBase { - - private static final String BUCKET = temporaryBucketName(GetObjectIntegrationTest.class); - - private static File file; - - @BeforeClass - public static void setupFixture() throws IOException { - createBucket(BUCKET); - file = new RandomTempFile(10_000); - } - - @AfterClass - public static void tearDownFixture() { - deleteBucketAndAllContents(BUCKET); - file.delete(); - } - +public class SyncServerSideEncryptionIntegrationTest extends ServerSideEncryptionIntegrationTestBase { @Test public void sse_AES256_succeeds() throws Exception { String key = UUID.randomUUID().toString(); @@ -124,15 +99,23 @@ public void sse_customerManaged_succeeds() { SdkAsserts.assertFileEqualsStream(file, response); } - private static byte[] generateSecretKey() { - KeyGenerator generator; - try { - generator = KeyGenerator.getInstance("AES"); - generator.init(256, new SecureRandom()); - return generator.generateKey().getEncoded(); - } catch (Exception e) { - fail("Unable to generate symmetric key: " + e.getMessage()); - return null; - } + @Test + public void sse_onBucket_succeeds() throws FileNotFoundException { + String key = UUID.randomUUID().toString(); + + PutObjectRequest request = PutObjectRequest.builder() + .key(key) + .bucket(BUCKET_WITH_SSE) + .build(); + + s3.putObject(request, file.toPath()); + + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .key(key) + .bucket(BUCKET_WITH_SSE) + .build(); + + String response = s3.getObject(getObjectRequest, ResponseTransformer.toBytes()).asUtf8String(); + SdkAsserts.assertStringEqualsStream(response, new FileInputStream(file)); } } diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadLargeObjectIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadLargeObjectIntegrationTest.java index 5a4c79de7720..c6f9db74bb30 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadLargeObjectIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadLargeObjectIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartIntegrationTest.java index 23f94028d2e0..e3963ddfb317 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartTestBase.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartTestBase.java index 12285fccbac0..9ebce247c716 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartTestBase.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UploadMultiplePartTestBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UrlEncodingIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UrlEncodingIntegrationTest.java new file mode 100644 index 000000000000..e988c458c2b0 --- /dev/null +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UrlEncodingIntegrationTest.java @@ -0,0 +1,134 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; + +import org.apache.commons.lang3.RandomStringUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.EncodingType; +import software.amazon.awssdk.services.s3.model.ListMultipartUploadsResponse; +import software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse; +import software.amazon.awssdk.services.s3.model.ListObjectsResponse; +import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.UploadPartResponse; + +/** + * Integration tests for the operations that support encoding type + */ +public class UrlEncodingIntegrationTest extends S3IntegrationTestBase { + /** + * The name of the bucket created, used, and deleted by these tests. + */ + private static final String BUCKET_NAME = temporaryBucketName(UrlEncodingIntegrationTest.class); + private static final String KEY_NAME_WITH_SPECIAL_CHARS = "filename_@_=_&_?_+_)_.temp"; + + @BeforeClass + public static void createResources() { + createBucket(BUCKET_NAME); + s3.putObject(PutObjectRequest.builder() + .bucket(BUCKET_NAME) + .key(KEY_NAME_WITH_SPECIAL_CHARS) + .build(), RequestBody.fromString(RandomStringUtils.random(1000))); + } + + /** + * Releases all resources created in this test. + */ + @AfterClass + public static void tearDown() { + deleteBucketAndAllContents(BUCKET_NAME); + } + + @Test + public void listObjectVersionsWithUrlEncodingType_shouldDecode() { + ListObjectVersionsResponse listObjectVersionsResponse = + s3.listObjectVersions(b -> b.bucket(BUCKET_NAME).encodingType(EncodingType.URL)); + listObjectVersionsResponse.versions().forEach(v -> assertKeyIsDecoded(v.key())); + + ListObjectVersionsResponse asyncResponse = + s3Async.listObjectVersions(b -> b.bucket(BUCKET_NAME).encodingType(EncodingType.URL)).join(); + + asyncResponse.versions().forEach(v -> assertKeyIsDecoded(v.key())); + } + + @Test + public void listObjectV2WithUrlEncodingType_shouldDecode() { + ListObjectsV2Response listObjectsV2Response = + s3.listObjectsV2(b -> b.bucket(BUCKET_NAME).encodingType(EncodingType.URL)); + + listObjectsV2Response.contents().forEach(c -> assertKeyIsDecoded(c.key())); + ListObjectVersionsResponse asyncResponse = + s3Async.listObjectVersions(b -> b.bucket(BUCKET_NAME).encodingType(EncodingType.URL)).join(); + + asyncResponse.versions().forEach(v -> assertKeyIsDecoded(v.key())); + } + + @Test + public void listObjectWithUrlEncodingType_shouldDecode() { + ListObjectsResponse listObjectsV2Response = + s3.listObjects(b -> b.bucket(BUCKET_NAME).encodingType(EncodingType.URL)); + + listObjectsV2Response.contents().forEach(c -> assertKeyIsDecoded(c.key())); + ListObjectVersionsResponse asyncResponse = + s3Async.listObjectVersions(b -> b.bucket(BUCKET_NAME).encodingType(EncodingType.URL)).join(); + + asyncResponse.versions().forEach(v -> assertKeyIsDecoded(v.key())); + } + + @Test + public void listMultipartUploadsWithUrlEncodingType_shouldDecode() { + String uploaddId = null; + try { + CreateMultipartUploadResponse multipartUploadResponse = + s3.createMultipartUpload(b -> b.bucket(BUCKET_NAME).key(KEY_NAME_WITH_SPECIAL_CHARS)); + uploaddId = multipartUploadResponse.uploadId(); + + String finalUploadId = uploaddId; + UploadPartResponse uploadPartResponse = s3.uploadPart(b -> b.bucket(BUCKET_NAME) + .key(KEY_NAME_WITH_SPECIAL_CHARS) + .partNumber(1) + .uploadId(finalUploadId), + RequestBody.fromString(RandomStringUtils.random(1000))); + + + ListMultipartUploadsResponse listMultipartUploadsResponse = + s3.listMultipartUploads(b -> b.encodingType(EncodingType.URL).bucket(BUCKET_NAME)); + + listMultipartUploadsResponse.uploads().forEach(upload -> assertThat(upload.key()).isEqualTo(KEY_NAME_WITH_SPECIAL_CHARS)); + + ListMultipartUploadsResponse asyncListMultipartUploadsResponse = + s3Async.listMultipartUploads(b -> b.encodingType(EncodingType.URL).bucket(BUCKET_NAME)).join(); + + asyncListMultipartUploadsResponse.uploads().forEach(upload -> assertKeyIsDecoded(upload.key())); + } finally { + if (uploaddId != null) { + String finalUploadId = uploaddId; + s3.abortMultipartUpload(b -> b.bucket(BUCKET_NAME).key(KEY_NAME_WITH_SPECIAL_CHARS).uploadId(finalUploadId)); + } + } + } + + private void assertKeyIsDecoded(String key) { + assertThat(key).isEqualTo(KEY_NAME_WITH_SPECIAL_CHARS); + } +} diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UserMetadataIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UserMetadataIntegrationTest.java index 94e82315769f..e24c029da125 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/UserMetadataIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/UserMetadataIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/signer/AwsS3V4SignerIntegrationTest.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/signer/AwsS3V4SignerIntegrationTest.java index 8fa568b44389..2cab5a15e4c8 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/signer/AwsS3V4SignerIntegrationTest.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/signer/AwsS3V4SignerIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/java/software/amazon/awssdk/services/s3/utils/S3TestUtils.java b/services/s3/src/it/java/software/amazon/awssdk/services/s3/utils/S3TestUtils.java index 8fe701564c95..952fbccdc701 100644 --- a/services/s3/src/it/java/software/amazon/awssdk/services/s3/utils/S3TestUtils.java +++ b/services/s3/src/it/java/software/amazon/awssdk/services/s3/utils/S3TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/it/resources/log4j2.xml b/services/s3/src/it/resources/log4j2.xml index 784a3e2dbf93..80ace9124cfc 100644 --- a/services/s3/src/it/resources/log4j2.xml +++ b/services/s3/src/it/resources/log4j2.xml @@ -1,4 +1,19 @@ + + diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java index 5bf00e558fbe..9e48a335baec 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Configuration.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,6 +20,10 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; import software.amazon.awssdk.core.ServiceConfiguration; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.services.s3.internal.FieldWithDefault; +import software.amazon.awssdk.services.s3.internal.usearnregion.UseArnRegionProviderChain; import software.amazon.awssdk.services.s3.model.PutBucketAccelerateConfigurationRequest; import software.amazon.awssdk.utils.builder.CopyableBuilder; import software.amazon.awssdk.utils.builder.ToCopyableBuilder; @@ -55,21 +59,36 @@ public final class S3Configuration implements ServiceConfiguration, ToCopyableBu */ private static final boolean DEFAULT_CHUNKED_ENCODING_ENABLED = true; - private final boolean pathStyleAccessEnabled; - private final boolean accelerateModeEnabled; - private final boolean dualstackEnabled; - private final boolean checksumValidationEnabled; - private final boolean chunkedEncodingEnabled; + private final FieldWithDefault pathStyleAccessEnabled; + private final FieldWithDefault accelerateModeEnabled; + private final FieldWithDefault dualstackEnabled; + private final FieldWithDefault checksumValidationEnabled; + private final FieldWithDefault chunkedEncodingEnabled; + private final FieldWithDefault useArnRegionEnabled; + private final FieldWithDefault profileFile; + private final FieldWithDefault profileName; private S3Configuration(DefaultS3ServiceConfigurationBuilder builder) { - this.dualstackEnabled = resolveBoolean(builder.dualstackEnabled, DEFAULT_DUALSTACK_ENABLED); - this.accelerateModeEnabled = resolveBoolean(builder.accelerateModeEnabled, DEFAULT_ACCELERATE_MODE_ENABLED); - this.pathStyleAccessEnabled = resolveBoolean(builder.pathStyleAccessEnabled, DEFAULT_PATH_STYLE_ACCESS_ENABLED); - this.checksumValidationEnabled = resolveBoolean(builder.checksumValidationEnabled, DEFAULT_CHECKSUM_VALIDATION_ENABLED); - if (accelerateModeEnabled && pathStyleAccessEnabled) { + this.dualstackEnabled = FieldWithDefault.create(builder.dualstackEnabled, DEFAULT_DUALSTACK_ENABLED); + this.accelerateModeEnabled = FieldWithDefault.create(builder.accelerateModeEnabled, DEFAULT_ACCELERATE_MODE_ENABLED); + this.pathStyleAccessEnabled = FieldWithDefault.create(builder.pathStyleAccessEnabled, DEFAULT_PATH_STYLE_ACCESS_ENABLED); + this.checksumValidationEnabled = FieldWithDefault.create(builder.checksumValidationEnabled, + DEFAULT_CHECKSUM_VALIDATION_ENABLED); + this.chunkedEncodingEnabled = FieldWithDefault.create(builder.chunkedEncodingEnabled, DEFAULT_CHUNKED_ENCODING_ENABLED); + this.profileFile = FieldWithDefault.createLazy(builder.profileFile, ProfileFile::defaultProfileFile); + this.profileName = FieldWithDefault.create(builder.profileName, + ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow()); + this.useArnRegionEnabled = FieldWithDefault.createLazy(builder.useArnRegionEnabled, this::resolveUserArnRegionEnabled); + + if (accelerateModeEnabled() && pathStyleAccessEnabled()) { throw new IllegalArgumentException("Accelerate mode cannot be used with path style addressing"); } - this.chunkedEncodingEnabled = resolveBoolean(builder.chunkedEncodingEnabled, DEFAULT_CHUNKED_ENCODING_ENABLED); + } + + private boolean resolveUserArnRegionEnabled() { + return UseArnRegionProviderChain.create(this.profileFile.value(), this.profileName.value()) + .resolveUseArnRegion() + .orElse(false); } /** @@ -99,7 +118,7 @@ public static Builder builder() { * @return True is the client should always use path-style access */ public boolean pathStyleAccessEnabled() { - return pathStyleAccessEnabled; + return pathStyleAccessEnabled.value(); } /** @@ -115,7 +134,7 @@ public boolean pathStyleAccessEnabled() { * @return True if accelerate mode is enabled. */ public boolean accelerateModeEnabled() { - return accelerateModeEnabled; + return accelerateModeEnabled.value(); } /** @@ -132,11 +151,11 @@ public boolean accelerateModeEnabled() { * @return True if the client will use the dualstack endpoints */ public boolean dualstackEnabled() { - return dualstackEnabled; + return dualstackEnabled.value(); } public boolean checksumValidationEnabled() { - return checksumValidationEnabled; + return checksumValidationEnabled.value(); } /** @@ -150,23 +169,36 @@ public boolean checksumValidationEnabled() { * @return True if chunked encoding should be used. */ public boolean chunkedEncodingEnabled() { - return chunkedEncodingEnabled; + return chunkedEncodingEnabled.value(); } - private boolean resolveBoolean(Boolean customerSuppliedValue, boolean defaultValue) { - return customerSuppliedValue == null ? defaultValue : customerSuppliedValue; + /** + * Returns whether the client is allowed to make cross-region calls when an S3 Access Point ARN has a different + * region to the one configured on the client. + *

    + * @return True if a different region in the ARN can be used. + */ + public boolean useArnRegionEnabled() { + return useArnRegionEnabled.value(); } @Override public Builder toBuilder() { return builder() - .dualstackEnabled(dualstackEnabled) - .accelerateModeEnabled(accelerateModeEnabled) - .pathStyleAccessEnabled(pathStyleAccessEnabled); + .dualstackEnabled(dualstackEnabled.valueOrNullIfDefault()) + .accelerateModeEnabled(accelerateModeEnabled.valueOrNullIfDefault()) + .pathStyleAccessEnabled(pathStyleAccessEnabled.valueOrNullIfDefault()) + .checksumValidationEnabled(checksumValidationEnabled.valueOrNullIfDefault()) + .chunkedEncodingEnabled(chunkedEncodingEnabled.valueOrNullIfDefault()) + .useArnRegionEnabled(useArnRegionEnabled.valueOrNullIfDefault()) + .profileFile(profileFile.valueOrNullIfDefault()) + .profileName(profileName.valueOrNullIfDefault()); } @NotThreadSafe - public interface Builder extends CopyableBuilder { // (8) + public interface Builder extends CopyableBuilder { + Boolean dualstackEnabled(); + /** * Option to enable using the dualstack endpoints when accessing S3. Dualstack * should be enabled if you want to use IPv6. @@ -179,6 +211,8 @@ public interface Builder extends CopyableBuilder { // */ Builder dualstackEnabled(Boolean dualstackEnabled); + Boolean accelerateModeEnabled(); + /** * Option to enable using the accelerate enedpoint when accessing S3. Accelerate * endpoints allow faster transfer of objects by using Amazon CloudFront's @@ -192,6 +226,8 @@ public interface Builder extends CopyableBuilder { // */ Builder accelerateModeEnabled(Boolean accelerateModeEnabled); + Boolean pathStyleAccessEnabled(); + /** * Option to enable using path style access for accessing S3 objects * instead of DNS style access. DNS style access is preferred as it @@ -206,6 +242,8 @@ public interface Builder extends CopyableBuilder { // */ Builder pathStyleAccessEnabled(Boolean pathStyleAccessEnabled); + Boolean checksumValidationEnabled(); + /** * Option to disable doing a validation of the checksum of an object stored in S3. * @@ -217,6 +255,8 @@ public interface Builder extends CopyableBuilder { // */ Builder checksumValidationEnabled(Boolean checksumValidationEnabled); + Boolean chunkedEncodingEnabled(); + /** * Option to enable using chunked encoding when signing the request * payload for {@link @@ -226,21 +266,68 @@ public interface Builder extends CopyableBuilder { // * @see S3Configuration#chunkedEncodingEnabled() */ Builder chunkedEncodingEnabled(Boolean chunkedEncodingEnabled); - } - private static final class DefaultS3ServiceConfigurationBuilder implements Builder { + Boolean useArnRegionEnabled(); + + /** + * If an S3 resource ARN is passed in as the target of an S3 operation that has a different region to the one + * the client was configured with, this flag must be set to 'true' to permit the client to make a + * cross-region call to the region specified in the ARN otherwise an exception will be thrown. + * + * @see S3Configuration#useArnRegionEnabled() + */ + Builder useArnRegionEnabled(Boolean useArnRegionEnabled); + ProfileFile profileFile(); + + /** + * The profile file that should be consulted to determine the default value of {@link #useArnRegionEnabled(Boolean)}. + * This is not used, if the {@link #useArnRegionEnabled(Boolean)} is configured. + * + *

    + * By default, the {@link ProfileFile#defaultProfileFile()} is used. + *

    + */ + Builder profileFile(ProfileFile profileFile); + + String profileName(); + + /** + * The profile name that should be consulted to determine the default value of {@link #useArnRegionEnabled(Boolean)}. + * This is not used, if the {@link #useArnRegionEnabled(Boolean)} is configured. + * + *

    + * By default, the {@link ProfileFileSystemSetting#AWS_PROFILE} is used. + *

    + */ + Builder profileName(String profileName); + } + + static final class DefaultS3ServiceConfigurationBuilder implements Builder { private Boolean dualstackEnabled; private Boolean accelerateModeEnabled; private Boolean pathStyleAccessEnabled; private Boolean checksumValidationEnabled; private Boolean chunkedEncodingEnabled; + private Boolean useArnRegionEnabled; + private ProfileFile profileFile; + private String profileName; + + @Override + public Boolean dualstackEnabled() { + return dualstackEnabled; + } public Builder dualstackEnabled(Boolean dualstackEnabled) { this.dualstackEnabled = dualstackEnabled; return this; } + @Override + public Boolean accelerateModeEnabled() { + return accelerateModeEnabled; + } + public void setDualstackEnabled(Boolean dualstackEnabled) { dualstackEnabled(dualstackEnabled); } @@ -250,6 +337,11 @@ public Builder accelerateModeEnabled(Boolean accelerateModeEnabled) { return this; } + @Override + public Boolean pathStyleAccessEnabled() { + return pathStyleAccessEnabled; + } + public void setAccelerateModeEnabled(Boolean accelerateModeEnabled) { accelerateModeEnabled(accelerateModeEnabled); } @@ -259,6 +351,11 @@ public Builder pathStyleAccessEnabled(Boolean pathStyleAccessEnabled) { return this; } + @Override + public Boolean checksumValidationEnabled() { + return checksumValidationEnabled; + } + public void setPathStyleAccessEnabled(Boolean pathStyleAccessEnabled) { pathStyleAccessEnabled(pathStyleAccessEnabled); } @@ -268,6 +365,11 @@ public Builder checksumValidationEnabled(Boolean checksumValidationEnabled) { return this; } + @Override + public Boolean chunkedEncodingEnabled() { + return chunkedEncodingEnabled; + } + public void setChecksumValidationEnabled(Boolean checksumValidationEnabled) { checksumValidationEnabled(checksumValidationEnabled); } @@ -277,10 +379,46 @@ public Builder chunkedEncodingEnabled(Boolean chunkedEncodingEnabled) { return this; } + @Override + public Boolean useArnRegionEnabled() { + return useArnRegionEnabled; + } + public void setChunkedEncodingEnabled(Boolean chunkedEncodingEnabled) { chunkedEncodingEnabled(chunkedEncodingEnabled); } + public Builder useArnRegionEnabled(Boolean useArnRegionEnabled) { + this.useArnRegionEnabled = useArnRegionEnabled; + return this; + } + + @Override + public ProfileFile profileFile() { + return profileFile; + } + + @Override + public Builder profileFile(ProfileFile profileFile) { + this.profileFile = profileFile; + return this; + } + + @Override + public String profileName() { + return profileName; + } + + @Override + public Builder profileName(String profileName) { + this.profileName = profileName; + return this; + } + + public void setUseArnRegionEnabled(Boolean useArnRegionEnabled) { + useArnRegionEnabled(useArnRegionEnabled); + } + public S3Configuration build() { return new S3Configuration(this); } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3SystemSetting.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3SystemSetting.java new file mode 100644 index 000000000000..239df4aec35c --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3SystemSetting.java @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3; + +import software.amazon.awssdk.annotations.SdkProtectedApi; +import software.amazon.awssdk.utils.SystemSetting; + +/** + * S3 specific system setting + */ +@SdkProtectedApi +public enum S3SystemSetting implements SystemSetting { + + AWS_S3_USE_ARN_REGION("aws.s3UseArnRegion", null); + + private final String systemProperty; + private final String defaultValue; + + S3SystemSetting(String systemProperty, String defaultValue) { + this.systemProperty = systemProperty; + this.defaultValue = defaultValue; + } + + @Override + public String property() { + return systemProperty; + } + + @Override + public String environmentVariable() { + return name(); + } + + @Override + public String defaultValue() { + return defaultValue; + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java index 89596aca60d7..72c4b6c98589 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/S3Utilities.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -23,18 +23,21 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.awscore.client.config.AwsClientOption; -import software.amazon.awssdk.awscore.internal.EndpointUtils; +import software.amazon.awssdk.awscore.endpoint.DefaultServiceEndpointBuilder; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.protocols.core.OperationInfo; import software.amazon.awssdk.protocols.core.PathMarshaller; import software.amazon.awssdk.protocols.core.ProtocolUtils; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.internal.S3EndpointUtils; +import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetUrlRequest; import software.amazon.awssdk.utils.Validate; @@ -46,8 +49,8 @@ * *
      * S3Utilities utilities = S3Utilities.builder().region(Region.US_WEST_2).build()
    - * GetUrlRequest request = GetUrlRequest.builder().bucket("foo-bucket").key("key-without-spaces").build()
    - * URL url = pathStyleUtilities.getUrl(request);
    + * GetUrlRequest request = GetUrlRequest.builder().bucket("foo-bucket").key("key-without-spaces").build();
    + * URL url = utilities.getUrl(request);
      * 
    *

    * @@ -58,8 +61,8 @@ *
      * S3Client s3client = S3Client.create();
      * S3Utilities utilities = s3client.utilities();
    - * GetUrlRequest request = GetUrlRequest.builder().bucket("foo-bucket").key("key-without-spaces").build()
    - * URL url = pathStyleUtilities.getUrl(request);
    + * GetUrlRequest request = GetUrlRequest.builder().bucket("foo-bucket").key("key-without-spaces").build();
    + * URL url = utilities.getUrl(request);
      * 
    *

    * @@ -68,10 +71,10 @@ @Immutable @SdkPublicApi public final class S3Utilities { - private final Region region; - private final S3Configuration s3Configuration; + private final ProfileFile profileFile; + private final String profileName; /** * SDK currently validates that region is present while constructing {@link S3Utilities} object. @@ -80,6 +83,8 @@ public final class S3Utilities { private S3Utilities(Builder builder) { this.region = Validate.paramNotNull(builder.region, "Region"); this.s3Configuration = builder.s3Configuration; + this.profileFile = builder.profileFile; + this.profileName = builder.profileName; } /** @@ -95,6 +100,8 @@ static S3Utilities create(SdkClientConfiguration clientConfiguration) { return S3Utilities.builder() .region(clientConfiguration.option(AwsClientOption.AWS_REGION)) .s3Configuration((S3Configuration) clientConfiguration.option(SdkClientOption.SERVICE_CONFIGURATION)) + .profileFile(clientConfiguration.option(SdkClientOption.PROFILE_FILE)) + .profileName(clientConfiguration.option(SdkClientOption.PROFILE_NAME)) .build(); } @@ -140,19 +147,26 @@ public URL getUrl(Consumer getUrlRequest) { public URL getUrl(GetUrlRequest getUrlRequest) { Region resolvedRegion = resolveRegionForGetUrl(getUrlRequest); URI resolvedEndpoint = resolveEndpoint(getUrlRequest.endpoint(), resolvedRegion); + boolean endpointOverridden = getUrlRequest.endpoint() != null; SdkHttpFullRequest marshalledRequest = createMarshalledRequest(getUrlRequest, resolvedEndpoint); + GetObjectRequest getObjectRequest = GetObjectRequest.builder() + .bucket(getUrlRequest.bucket()) + .key(getUrlRequest.key()) + .build(); + SdkHttpRequest httpRequest = S3EndpointUtils.applyEndpointConfiguration(marshalledRequest, - getUrlRequest, + getObjectRequest, resolvedRegion, s3Configuration, - getUrlRequest.bucket()); + endpointOverridden) + .sdkHttpRequest(); try { return httpRequest.getUri().toURL(); } catch (MalformedURLException exception) { - throw SdkException.create(String.format("Generated URI is malformed: " + httpRequest.getUri()), + throw SdkException.create("Generated URI is malformed: " + httpRequest.getUri(), exception); } } @@ -170,7 +184,10 @@ private Region resolveRegionForGetUrl(GetUrlRequest getUrlRequest) { */ private URI resolveEndpoint(URI endpoint, Region region) { return endpoint != null ? endpoint - : EndpointUtils.buildEndpoint("https", "s3", region); + : new DefaultServiceEndpointBuilder("s3", "https").withRegion(region) + .withProfileFile(profileFile) + .withProfileName(profileName) + .getServiceEndpoint(); } /** @@ -202,6 +219,8 @@ public static final class Builder { private Region region; private S3Configuration s3Configuration; + private ProfileFile profileFile; + private String profileName; private Builder() { } @@ -232,6 +251,26 @@ public Builder s3Configuration(S3Configuration s3Configuration) { return this; } + /** + * The profile file from the {@link ClientOverrideConfiguration#profileFile()}. This is private and only used when the + * utilities is created via {@link S3Client#utilities()}. This is not currently public because it may be less confusing + * to support the full {@link ClientOverrideConfiguration} object in the future. + */ + private Builder profileFile(ProfileFile profileFile) { + this.profileFile = profileFile; + return this; + } + + /** + * The profile name from the {@link ClientOverrideConfiguration#profileName()}. This is private and only used when the + * utilities is created via {@link S3Client#utilities()}. This is not currently public because it may be less confusing + * to support the full {@link ClientOverrideConfiguration} object in the future. + */ + private Builder profileName(String profileName) { + this.profileName = profileName; + return this; + } + /** * Construct a {@link S3Utilities} object. */ diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingAsyncRequestBody.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingAsyncRequestBody.java index c4f262a1c710..48c56d82503c 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingAsyncRequestBody.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingAsyncRequestBody.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -42,6 +42,7 @@ public Optional contentLength() { @Override public void subscribe(Subscriber s) { + sdkChecksum.reset(); wrapped.subscribe(new ChecksumCalculatingSubscriber(s, sdkChecksum)); } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStream.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStream.java index 382f789865cb..f6fae1ff968f 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStream.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumConstant.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumConstant.java index 03872809faca..fbe49e94eae3 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumConstant.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumConstant.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -55,5 +55,6 @@ public final class ChecksumConstant { */ public static final int S3_MD5_CHECKSUM_LENGTH = 16; - private ChecksumConstant() {} + private ChecksumConstant() { + } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingInputStream.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingInputStream.java index 6b18f6403d7f..ab089377e8cc 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingInputStream.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -105,12 +105,12 @@ public int read(byte[] buf, int off, int len) throws IOException { int read = -1; if (lengthRead < strippedLength) { - long maxRead = Math.min((long) Integer.MAX_VALUE, strippedLength - lengthRead); - int maxIterRead = (int) Math.min(maxRead, (long) len); + long maxRead = Math.min(Integer.MAX_VALUE, strippedLength - lengthRead); + int maxIterRead = (int) Math.min(maxRead, len); read = inputStream.read(buf, off, maxIterRead); - int toUpdate = (int) Math.min(strippedLength - lengthRead, (long) read); + int toUpdate = (int) Math.min(strippedLength - lengthRead, read); if (toUpdate > 0) { checkSum.update(buf, off, toUpdate); diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisher.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisher.java index 2b453f4ec0c8..2c871470d84e 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisher.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidator.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidator.java index e74438c33a85..6c2afec748f0 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidator.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -32,6 +32,8 @@ import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.http.SdkHttpHeaders; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.internal.handlers.AsyncChecksumValidationInterceptor; @@ -84,14 +86,13 @@ public static boolean getObjectChecksumEnabledPerResponse(SdkRequest request, Sd * * @param expectedClientType - The expected client type for enabling checksums * @param executionAttributes - {@link ExecutionAttributes} to determine the actual client type - * @param sdkHttpHeaders A map of headers for a given request * @return If trailing checksums should be enabled for this request. */ - public static boolean putObjectChecksumEnabled(SdkRequest request, - ClientType expectedClientType, - ExecutionAttributes executionAttributes, - SdkHttpHeaders sdkHttpHeaders) { - if (!(request instanceof PutObjectRequest)) { + public static boolean shouldRecordChecksum(SdkRequest sdkRequest, + ClientType expectedClientType, + ExecutionAttributes executionAttributes, + SdkHttpRequest httpRequest) { + if (!(sdkRequest instanceof PutObjectRequest)) { return false; } @@ -101,21 +102,33 @@ public static boolean putObjectChecksumEnabled(SdkRequest request, return false; } - // S3 doesn't support trailing checksums for customer encryption - if (sdkHttpHeaders.firstMatchingHeader(SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER).isPresent()) { - return false; - } - // S3 doesn't support trailing checksums for KMS encrypted objects - if (sdkHttpHeaders.firstMatchingHeader(SERVER_SIDE_ENCRYPTION_HEADER) - .filter(h -> h.contains(AWS_KMS.toString())) - .isPresent()) { + if (hasServerSideEncryptionHeader(httpRequest)) { return false; } return checksumEnabledPerConfig(executionAttributes); } + public static boolean responseChecksumIsValid(SdkHttpResponse httpResponse) { + return !hasServerSideEncryptionHeader(httpResponse); + } + + private static boolean hasServerSideEncryptionHeader(SdkHttpHeaders httpRequest) { + // S3 doesn't support trailing checksums for customer encryption + if (httpRequest.firstMatchingHeader(SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER).isPresent()) { + return true; + } + + // S3 doesn't support trailing checksums for KMS encrypted objects + if (httpRequest.firstMatchingHeader(SERVER_SIDE_ENCRYPTION_HEADER) + .filter(h -> h.contains(AWS_KMS.toString())) + .isPresent()) { + return true; + } + return false; + } + /** * Client side validation for {@link PutObjectRequest} * @@ -131,7 +144,9 @@ public static void validatePutObjectChecksum(PutObjectResponse response, Executi byte[] ssHash = Base16Lower.decode(response.eTag().replace("\"", "")); if (!Arrays.equals(digest, ssHash)) { - throw SdkClientException.create("Data read has a different checksum than expected."); + throw SdkClientException.create( + String.format("Data read has a different checksum than expected. Was 0x%s, but expected 0x%s", + BinaryUtils.toHex(digest), BinaryUtils.toHex(ssHash))); } } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/BucketUtils.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/BucketUtils.java index 781d6d202b7e..9e6a31a7531d 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/BucketUtils.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/BucketUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/ConfiguredS3SdkHttpRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/ConfiguredS3SdkHttpRequest.java new file mode 100644 index 000000000000..72f31f2e6a54 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/ConfiguredS3SdkHttpRequest.java @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +@SdkInternalApi +public class ConfiguredS3SdkHttpRequest + implements ToCopyableBuilder { + private final SdkHttpRequest sdkHttpRequest; + private final Region signingRegionModification; + + private ConfiguredS3SdkHttpRequest(Builder builder) { + this.sdkHttpRequest = Validate.notNull(builder.sdkHttpRequest, "sdkHttpRequest"); + this.signingRegionModification = builder.signingRegionModification; + } + + public static Builder builder() { + return new Builder(); + } + + public SdkHttpRequest sdkHttpRequest() { + return sdkHttpRequest; + } + + public Optional signingRegionModification() { + return Optional.ofNullable(signingRegionModification); + } + + @Override + public Builder toBuilder() { + return builder().sdkHttpRequest(sdkHttpRequest).signingRegionModification(signingRegionModification); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + ConfiguredS3SdkHttpRequest that = (ConfiguredS3SdkHttpRequest) o; + + if (sdkHttpRequest != null ? ! sdkHttpRequest.equals(that.sdkHttpRequest) : that.sdkHttpRequest != null) { + return false; + } + return signingRegionModification != null ? + signingRegionModification.equals(that.signingRegionModification) + : that.signingRegionModification == null; + } + + @Override + public int hashCode() { + int result = sdkHttpRequest != null ? sdkHttpRequest.hashCode() : 0; + result = 31 * result + (signingRegionModification != null ? signingRegionModification.hashCode() : 0); + return result; + } + + public static class Builder implements CopyableBuilder { + private SdkHttpRequest sdkHttpRequest; + private Region signingRegionModification; + + private Builder() { + } + + public Builder sdkHttpRequest(SdkHttpRequest sdkHttpRequest) { + this.sdkHttpRequest = sdkHttpRequest; + return this; + } + + public Builder signingRegionModification(Region signingRegionModification) { + this.signingRegionModification = signingRegionModification; + return this; + } + + @Override + public ConfiguredS3SdkHttpRequest build() { + return new ConfiguredS3SdkHttpRequest(this); + } + } + +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/FieldWithDefault.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/FieldWithDefault.java new file mode 100644 index 000000000000..9c1f9ca8ee17 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/FieldWithDefault.java @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal; + +import java.util.function.Supplier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Lazy; + +/** + * A helper class for setting a field's value to a default if it isn't specified, while still keeping track of whether the value + * was from the default or from the field. + * + * For example, a "profile name" field-with-default might be set to "null" with a default of "foo". {@link #value()} returns + * "foo", while {@link #isDefault()} can be used to keep track of the fact that the value was from the default. + */ +@SdkInternalApi +public abstract class FieldWithDefault { + private FieldWithDefault(){ + } + + /** + * Create a {@link FieldWithDefault} using the provided field and its default value. If the field is null, the default value + * will be returned by {@link #value()} and {@link #isDefault()} will return true. If the field is not null, the field value + * will be returned by {@link #value()} and {@link #isDefault()} will return false. + * + * @see #createLazy(Object, Supplier) + */ + public static FieldWithDefault create(T field, T defaultValue) { + return new Impl<>(field, defaultValue); + } + + /** + * Create a {@link FieldWithDefault} using the provided field and its default value. If the field is null, the default value + * will be returned by {@link #value()} and {@link #isDefault()} will return true. If the field is not null, the field value + * will be returned by {@link #value()} and {@link #isDefault()} will return false. + * + *

    This differs from {@link #create(Object, Object)} in that the default value won't be resolved if the provided field is + * not null. The default value also won't be resolved until the first {@link #value()} call. This is useful for delaying + * expensive calculations until right before they're needed. + */ + public static FieldWithDefault createLazy(T field, Supplier defaultValue) { + return new LazyImpl<>(field, defaultValue); + } + + /** + * Retrieve the value of this field. + */ + public abstract T value(); + + /** + * True, if the value returned by {@link #value()} is the default value (i.e. the field is null). False otherwise. + */ + public abstract boolean isDefault(); + + /** + * Return the field exactly as it was specified when the field-with-default was created. If the field was null, this will + * return null. This will not resolve the default if this is a field from {@link #createLazy(Object, Supplier)}. + */ + public abstract T valueOrNullIfDefault(); + + private static class Impl extends FieldWithDefault { + private final T value; + private final boolean isDefault; + + private Impl(T field, T defaultValue) { + this.value = field != null ? field : defaultValue; + this.isDefault = field == null; + } + + @Override + public T value() { + return value; + } + + @Override + public boolean isDefault() { + return isDefault; + } + + @Override + public T valueOrNullIfDefault() { + return isDefault ? null : value; + } + } + + private static class LazyImpl extends FieldWithDefault { + private final Lazy value; + private final boolean isDefault; + + private LazyImpl(T field, Supplier defaultValue) { + this.value = field != null ? new Lazy<>(() -> field) : new Lazy<>(defaultValue); + this.isDefault = field == null; + } + + @Override + public T value() { + return value.getValue(); + } + + @Override + public boolean isDefault() { + return isDefault; + } + + @Override + public T valueOrNullIfDefault() { + return isDefault ? null : value.getValue(); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3EndpointUtils.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3EndpointUtils.java index f20068fc8a2e..41d1455035fc 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3EndpointUtils.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3EndpointUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -22,13 +22,22 @@ import java.util.Arrays; import java.util.List; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.arns.Arn; +import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.regions.PartitionMetadata; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.RegionMetadata; import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.internal.resource.S3AccessPointBuilder; +import software.amazon.awssdk.services.s3.internal.resource.S3AccessPointResource; +import software.amazon.awssdk.services.s3.internal.resource.S3ArnConverter; +import software.amazon.awssdk.services.s3.internal.resource.S3Resource; +import software.amazon.awssdk.services.s3.internal.resource.S3ResourceType; import software.amazon.awssdk.services.s3.model.CreateBucketRequest; import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; import software.amazon.awssdk.services.s3.model.ListBucketsRequest; +import software.amazon.awssdk.utils.Validate; /** * Utilities for working with Amazon S3 bucket names, such as validation and @@ -47,11 +56,18 @@ private S3EndpointUtils() { * Returns a new instance of the given {@link SdkHttpRequest} by applying any endpoint changes based on * the given {@link S3Configuration} options. */ - public static SdkHttpRequest applyEndpointConfiguration(SdkHttpRequest request, - Object originalRequest, - Region region, - S3Configuration serviceConfiguration, - String bucketName) { + public static ConfiguredS3SdkHttpRequest applyEndpointConfiguration(SdkHttpRequest request, + SdkRequest originalRequest, + Region region, + S3Configuration serviceConfiguration, + boolean endpointOverridden) { + String bucketName = originalRequest.getValueForField("Bucket", String.class).orElse(null); + String key = originalRequest.getValueForField("Key", String.class).orElse(null); + + if (bucketName != null && isArn(bucketName)) { + return applyEndpointConfigurationForAccessPointArn(request, region, endpointOverridden, + serviceConfiguration, bucketName, key); + } SdkHttpRequest.Builder mutableRequest = request.toBuilder(); @@ -66,7 +82,108 @@ public static SdkHttpRequest applyEndpointConfiguration(SdkHttpRequest request, } } - return mutableRequest.build(); + return ConfiguredS3SdkHttpRequest.builder() + .sdkHttpRequest(mutableRequest.build()) + .build(); + } + + private static ConfiguredS3SdkHttpRequest applyEndpointConfigurationForAccessPointArn( + SdkHttpRequest request, + Region region, + boolean endpointOverridden, + S3Configuration serviceConfiguration, + String bucketName, + String key) { + + Arn resourceArn = Arn.fromString(bucketName); + S3Resource s3Resource = S3ArnConverter.create().convertArn(resourceArn); + + if (S3ResourceType.fromValue(s3Resource.type()) != S3ResourceType.ACCESS_POINT) { + throw new IllegalArgumentException("An ARN was passed as a bucket parameter to an S3 operation, " + + "however it does not appear to be a valid S3 access point ARN."); + } + + String arnRegion = resourceArn.region().orElseThrow(() -> new IllegalArgumentException( + "An S3 access point ARN must have a region")); + + if (isFipsRegion(region.toString())) { + throw new IllegalArgumentException("An access point ARN cannot be passed as a bucket parameter to an S3" + + " operation if the S3 client has been configured with a FIPS" + + " enabled region."); + } + + if (serviceConfiguration != null && serviceConfiguration.accelerateModeEnabled()) { + throw new IllegalArgumentException("An access point ARN cannot be passed as a bucket parameter to an S3 " + + "operation if the S3 client has been configured with accelerate mode" + + " enabled."); + } + + if (serviceConfiguration != null && serviceConfiguration.pathStyleAccessEnabled()) { + throw new IllegalArgumentException("An access point ARN cannot be passed as a bucket parameter to an S3 " + + "operation if the S3 client has been configured with path style " + + "addressing enabled."); + } + + if (endpointOverridden) { + throw new IllegalArgumentException("An access point ARN cannot be passed as a bucket parameter to an S3" + + " operation if the S3 client has been configured with an endpoint " + + "override."); + } + + if (serviceConfiguration == null || !serviceConfiguration.useArnRegionEnabled()) { + if (!region.id().equals(arnRegion)) { + throw new IllegalArgumentException( + String.format("The region field of the ARN being passed as a bucket parameter to an S3 operation " + + "does not match the region the client was configured with. To enable this " + + "behavior and prevent this exception set 'useArnRegionEnabled' to true in the " + + "configuration when building the S3 client. Provided region: '%s'; client region:" + + " '%s'.", arnRegion, region)); + } + } + + PartitionMetadata clientPartitionMetadata = PartitionMetadata.of(region); + String clientPartition = clientPartitionMetadata.id(); + + if (clientPartition == null || clientPartition.isEmpty() || !s3Resource.partition().isPresent() + || !clientPartition.equals(s3Resource.partition().get())) { + throw new IllegalArgumentException( + String.format("The partition field of the ARN being passed as a bucket parameter to an S3 operation " + + "does not match the partition the S3 client has been configured with. Provided " + + "partition: '%s'; client partition: '%s'.", s3Resource.partition().orElse(""), + clientPartition)); + } + + S3AccessPointResource s3EndpointResource = + Validate.isInstanceOf(S3AccessPointResource.class, s3Resource, + "An ARN was passed as a bucket parameter to an S3 operation, however it does not " + + "appear to be a valid S3 access point ARN."); + + // DualstackEnabled considered false by default + boolean dualstackEnabled = serviceConfiguration != null && serviceConfiguration.dualstackEnabled(); + + URI accessPointUri = + S3AccessPointBuilder.create() + .accessPointName(s3EndpointResource.accessPointName()) + .accountId( + s3EndpointResource.accountId().orElseThrow(() -> new IllegalArgumentException( + "An S3 access point ARN must have an account ID"))) + .region(arnRegion) + .protocol(request.protocol()) + .domain(clientPartitionMetadata.dnsSuffix()) + .dualstackEnabled(dualstackEnabled) + .toUri(); + + SdkHttpRequest httpRequest = request.toBuilder() + .protocol(accessPointUri.getScheme()) + .host(accessPointUri.getHost()) + .port(accessPointUri.getPort()) + .encodedPath(key) + .build(); + + return ConfiguredS3SdkHttpRequest.builder() + .sdkHttpRequest(httpRequest) + .signingRegionModification(Region.of(arnRegion)) + .build(); } /** @@ -75,12 +192,14 @@ public static SdkHttpRequest applyEndpointConfiguration(SdkHttpRequest request, * a regional dualstack endpoint for IPV6 (i.e. s3.dualstack.us-east-1.amazonaws.com). */ private static URI resolveEndpoint(SdkHttpRequest request, - Object originalRequest, + SdkRequest originalRequest, Region region, S3Configuration serviceConfiguration) { - RegionMetadata regionMetadata = RegionMetadata.of(region); + String protocol = request.protocol(); + RegionMetadata regionMetadata = RegionMetadata.of(region); + if (isAccelerateEnabled(serviceConfiguration) && isAccelerateSupported(originalRequest)) { return accelerateEndpoint(serviceConfiguration, regionMetadata, protocol); } @@ -128,7 +247,7 @@ private static boolean isAccelerateEnabled(S3Configuration serviceConfiguration) * @param originalRequest Request object to identify the operation. * @return True if accelerate is supported for the given operation, false if not. */ - private static boolean isAccelerateSupported(Object originalRequest) { + private static boolean isAccelerateSupported(SdkRequest originalRequest) { return !ACCELERATE_DISABLED_OPERATIONS.contains(originalRequest.getClass()); } @@ -149,4 +268,12 @@ private static URI toUri(String protocol, String endpoint) { throw new IllegalArgumentException(e); } } + + private static boolean isArn(String s) { + return s.startsWith("arn:"); + } + + private static boolean isFipsRegion(String region) { + return region.startsWith("fips-") || region.endsWith("-fips"); + } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3HttpConfigurationOptions.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3HttpConfigurationOptions.java deleted file mode 100644 index 02d9dc8129b8..000000000000 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/S3HttpConfigurationOptions.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal; - -import java.time.Duration; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.http.SdkHttpConfigurationOption; -import software.amazon.awssdk.utils.AttributeMap; - -/** - * S3 specific http configurations - */ -@SdkInternalApi -public final class S3HttpConfigurationOptions { - private static final AttributeMap OPTIONS = AttributeMap - .builder() - .put(SdkHttpConfigurationOption.CONNECTION_MAX_IDLE_TIMEOUT, Duration.ofSeconds(5)) - .build(); - - private S3HttpConfigurationOptions() { - } - - public static AttributeMap defaultHttpConfig() { - return OPTIONS; - } -} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/TaggingAdapter.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/TaggingAdapter.java index 85a46964fbe6..c9d97e6e4090 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/TaggingAdapter.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/TaggingAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AddContentMd5HeaderInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AddContentMd5HeaderInterceptor.java index c113782c1eab..8d2510e329de 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AddContentMd5HeaderInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AddContentMd5HeaderInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptor.java index ec94f4b40912..af691aae4ee5 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.CONTENT_LENGTH_HEADER; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.CHECKSUM; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.getObjectChecksumEnabledPerResponse; -import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.putObjectChecksumEnabled; +import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.responseChecksumIsValid; +import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.shouldRecordChecksum; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.validatePutObjectChecksum; import java.nio.ByteBuffer; @@ -30,6 +31,7 @@ import software.amazon.awssdk.core.checksums.Md5Checksum; import software.amazon.awssdk.core.checksums.SdkChecksum; import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttribute; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.services.s3.checksums.ChecksumCalculatingAsyncRequestBody; @@ -38,16 +40,16 @@ @SdkInternalApi public final class AsyncChecksumValidationInterceptor implements ExecutionInterceptor { + private static ExecutionAttribute ASYNC_RECORDING_CHECKSUM = new ExecutionAttribute<>("asyncRecordingChecksum"); @Override public Optional modifyAsyncHttpContent(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { + boolean shouldRecordChecksum = shouldRecordChecksum(context.request(), ASYNC, executionAttributes, context.httpRequest()); - boolean putObjectTrailingChecksumsEnabled = - putObjectChecksumEnabled(context.request(), ASYNC, executionAttributes, context.httpRequest()); - - if (putObjectTrailingChecksumsEnabled && context.asyncRequestBody().isPresent()) { + if (shouldRecordChecksum && context.asyncRequestBody().isPresent()) { SdkChecksum checksum = new Md5Checksum(); + executionAttributes.putAttribute(ASYNC_RECORDING_CHECKSUM, true); executionAttributes.putAttribute(CHECKSUM, checksum); return Optional.of(new ChecksumCalculatingAsyncRequestBody(context.asyncRequestBody().get(), checksum)); } @@ -58,7 +60,6 @@ public Optional modifyAsyncHttpContent(Context.ModifyHttpReque @Override public Optional> modifyAsyncHttpResponseContent(Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes) { - if (getObjectChecksumEnabledPerResponse(context.request(), context.httpResponse()) && context.responsePublisher().isPresent()) { long contentLength = context.httpResponse() @@ -78,11 +79,10 @@ public Optional> modifyAsyncHttpResponseContent(Context.Mo @Override public void afterUnmarshalling(Context.AfterUnmarshalling context, ExecutionAttributes executionAttributes) { + boolean recordingChecksum = Boolean.TRUE.equals(executionAttributes.getAttribute(ASYNC_RECORDING_CHECKSUM)); + boolean responseChecksumIsValid = responseChecksumIsValid(context.httpResponse()); - boolean putObjectChecksumsEnabled = - putObjectChecksumEnabled(context.request(), ASYNC, executionAttributes, context.httpRequest()); - - if (putObjectChecksumsEnabled) { + if (recordingChecksum && responseChecksumIsValid) { validatePutObjectChecksum((PutObjectResponse) context.response(), executionAttributes); } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptor.java index 81e525504a4f..5bb616f8cd0d 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptor.java index 5c678d2f2c82..94bf44339df1 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptor.java index 9a2e26b76eb4..3a94b74d9e14 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import static software.amazon.awssdk.utils.http.SdkHttpUtils.urlDecode; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import software.amazon.awssdk.annotations.SdkInternalApi; @@ -24,9 +25,14 @@ import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.services.s3.model.CommonPrefix; import software.amazon.awssdk.services.s3.model.EncodingType; +import software.amazon.awssdk.services.s3.model.ListMultipartUploadsResponse; +import software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse; import software.amazon.awssdk.services.s3.model.ListObjectsResponse; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.MultipartUpload; +import software.amazon.awssdk.services.s3.model.ObjectVersion; import software.amazon.awssdk.services.s3.model.S3Object; /** @@ -50,9 +56,19 @@ public SdkResponse modifyResponse(Context.ModifyResponse context, SdkResponse response = context.response(); if (shouldHandle(response)) { if (response instanceof ListObjectsResponse) { - response = modifyListObjectsResponse((ListObjectsResponse) response); - } else if (response instanceof ListObjectsV2Response) { - response = modifyListObjectsV2Response((ListObjectsV2Response) response); + return modifyListObjectsResponse((ListObjectsResponse) response); + } + + if (response instanceof ListObjectsV2Response) { + return modifyListObjectsV2Response((ListObjectsV2Response) response); + } + + if (response instanceof ListObjectVersionsResponse) { + return modifyListObjectVersionsResponse((ListObjectVersionsResponse) response); + } + + if (response instanceof ListMultipartUploadsResponse) { + return modifyListMultipartUploadsResponse((ListMultipartUploadsResponse) response); } } return response; @@ -67,30 +83,90 @@ private static boolean shouldHandle(SdkResponse sdkResponse) { // Elements to decode: Delimiter, Marker, Prefix, NextMarker, Key private static SdkResponse modifyListObjectsResponse(ListObjectsResponse response) { return response.toBuilder() - .delimiter(urlDecode(response.delimiter())) - .marker(urlDecode(response.delimiter())) - .prefix(urlDecode(response.prefix())) - .nextMarker(urlDecode(response.nextMarker())) - .contents(decodeContents(response.contents())) - .build(); + .delimiter(urlDecode(response.delimiter())) + .marker(urlDecode(response.marker())) + .prefix(urlDecode(response.prefix())) + .nextMarker(urlDecode(response.nextMarker())) + .contents(decodeContents(response.contents())) + .commonPrefixes(decodeCommonPrefixes(response.commonPrefixes())) + .build(); } // Elements to decode: Delimiter, Prefix, Key, and StartAfter private static SdkResponse modifyListObjectsV2Response(ListObjectsV2Response response) { return response.toBuilder() - .delimiter(urlDecode(response.delimiter())) - .prefix(urlDecode(response.prefix())) - .startAfter(urlDecode(response.startAfter())) - .contents(decodeContents(response.contents())) - .build(); + .delimiter(urlDecode(response.delimiter())) + .prefix(urlDecode(response.prefix())) + .startAfter(urlDecode(response.startAfter())) + .contents(decodeContents(response.contents())) + .commonPrefixes(decodeCommonPrefixes(response.commonPrefixes())) + .build(); + } + + // https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectVersions.html + // Elements to decode: Delimiter, KeyMarker, NextKeyMarker, Prefix + private SdkResponse modifyListObjectVersionsResponse(ListObjectVersionsResponse response) { + + return response.toBuilder() + .prefix(urlDecode(response.prefix())) + .keyMarker(urlDecode(response.keyMarker())) + .delimiter(urlDecode(response.delimiter())) + .nextKeyMarker(urlDecode(response.nextKeyMarker())) + .commonPrefixes(decodeCommonPrefixes(response.commonPrefixes())) + .versions(decodeObjectVersions(response.versions())) + .build(); + } + + // https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html + // Elements to decode: Delimiter, KeyMarker, NextKeyMarker, Prefix, Key + private SdkResponse modifyListMultipartUploadsResponse(ListMultipartUploadsResponse response) { + return response.toBuilder() + .delimiter(urlDecode(response.delimiter())) + .keyMarker(urlDecode(response.keyMarker())) + .nextKeyMarker(urlDecode(response.nextKeyMarker())) + .prefix(urlDecode(response.prefix())) + .commonPrefixes(decodeCommonPrefixes(response.commonPrefixes())) + .uploads(decodeMultipartUpload(response.uploads())) + .build(); + } private static List decodeContents(List contents) { if (contents == null) { return null; } - return contents.stream() - .map(o -> o.toBuilder().key(urlDecode(o.key())).build()) - .collect(Collectors.toList()); + return Collections.unmodifiableList(contents.stream() + .map(o -> o.toBuilder().key(urlDecode(o.key())).build()) + .collect(Collectors.toList())); + } + + private static List decodeObjectVersions(List objectVersions) { + if (objectVersions == null) { + return null; + } + + return Collections.unmodifiableList(objectVersions.stream() + .map(o -> o.toBuilder().key(urlDecode(o.key())).build()) + .collect(Collectors.toList())); + } + + private static List decodeCommonPrefixes(List commonPrefixes) { + if (commonPrefixes == null) { + return null; + } + + return Collections.unmodifiableList(commonPrefixes.stream() + .map(p -> p.toBuilder().prefix(urlDecode(p.prefix())).build()) + .collect(Collectors.toList())); + } + + private static List decodeMultipartUpload(List multipartUploads) { + if (multipartUploads == null) { + return null; + } + + return Collections.unmodifiableList(multipartUploads.stream() + .map(u -> u.toBuilder().key(urlDecode(u.key())).build()) + .collect(Collectors.toList())); } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DisableDoubleUrlEncodingInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DisableDoubleUrlEncodingInterceptor.java index 5547310f112e..211a44719e7b 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DisableDoubleUrlEncodingInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/DisableDoubleUrlEncodingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptor.java index ec11b919128c..4788782e9cf2 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptor.java index c18455351db6..95efceb68dd7 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.utils.Validate; @SdkInternalApi public final class EnableTrailingChecksumInterceptor implements ExecutionInterceptor { @@ -60,7 +61,11 @@ public SdkResponse modifyResponse(Context.ModifyResponse context, ExecutionAttri if (getObjectChecksumEnabledPerResponse(context.request(), httpResponse)) { GetObjectResponse getResponse = (GetObjectResponse) response; - return getResponse.toBuilder().contentLength(getResponse.contentLength() - S3_MD5_CHECKSUM_LENGTH).build(); + Long contentLength = getResponse.contentLength(); + Validate.notNull(contentLength, "Service returned null 'Content-Length'."); + return getResponse.toBuilder() + .contentLength(contentLength - S3_MD5_CHECKSUM_LENGTH) + .build(); } return response; diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java index 6c7bfe52c5b1..9b0fecca4137 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -18,12 +18,13 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.internal.ConfiguredS3SdkHttpRequest; import software.amazon.awssdk.services.s3.internal.S3EndpointUtils; @SdkInternalApi @@ -32,15 +33,17 @@ public final class EndpointAddressInterceptor implements ExecutionInterceptor { @Override public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { + ConfiguredS3SdkHttpRequest configuredRequest = + S3EndpointUtils.applyEndpointConfiguration( + context.httpRequest(), + context.request(), + executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION), + (S3Configuration) executionAttributes.getAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG), + Boolean.TRUE.equals(executionAttributes.getAttribute(SdkExecutionAttribute.ENDPOINT_OVERRIDDEN))); - SdkRequest sdkRequest = context.request(); + configuredRequest.signingRegionModification().ifPresent( + region -> executionAttributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, region)); - return S3EndpointUtils.applyEndpointConfiguration(context.httpRequest(), - sdkRequest, - executionAttributes.getAttribute(AwsExecutionAttribute.AWS_REGION), - (S3Configuration) executionAttributes - .getAttribute(AwsSignerExecutionAttribute.SERVICE_CONFIG), - sdkRequest.getValueForField("Bucket", String.class) - .orElse(null)); + return configuredRequest.sdkHttpRequest(); } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptor.java index 933cd8f3130a..b185d36439a0 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptor.java index d33d868a91f8..dc66f7fca380 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -18,11 +18,15 @@ import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; import java.io.InputStream; +import java.nio.ByteBuffer; import java.util.Optional; +import java.util.function.Predicate; +import org.reactivestreams.Publisher; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.internal.async.SdkPublishers; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.services.s3.model.GetBucketPolicyRequest; import software.amazon.awssdk.utils.IoUtils; @@ -33,24 +37,38 @@ */ @SdkInternalApi public final class GetBucketPolicyInterceptor implements ExecutionInterceptor { + private static final String XML_ENVELOPE_PREFIX = ""; + + private static final Predicate INTERCEPTOR_CONTEXT_PREDICATE = + context -> context.request() instanceof GetBucketPolicyRequest && context.httpResponse().isSuccessful(); @Override public Optional modifyHttpResponseContent(Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes) { - if (context.request() instanceof GetBucketPolicyRequest && context.httpResponse().isSuccessful()) { + if (INTERCEPTOR_CONTEXT_PREDICATE.test(context)) { String policy = context.responseBody() .map(r -> invokeSafely(() -> IoUtils.toUtf8String(r))) .orElse(null); if (policy != null) { - // Wrap in CDATA to deal with any escaping issues - String xml = String.format("" - + "", policy); + String xml = XML_ENVELOPE_PREFIX + policy + XML_ENVELOPE_SUFFIX; return Optional.of(AbortableInputStream.create(new StringInputStream(xml))); } } return context.responseBody(); } + + @Override + public Optional> modifyAsyncHttpResponseContent(Context.ModifyHttpResponse context, + ExecutionAttributes executionAttributes) { + if (INTERCEPTOR_CONTEXT_PREDICATE.test(context)) { + return context.responsePublisher().map( + body -> SdkPublishers.envelopeWrappedPublisher(body, XML_ENVELOPE_PREFIX, XML_ENVELOPE_SUFFIX)); + } + + return context.responsePublisher(); + } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptor.java index f207d365555e..92859b96dea9 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptor.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptor.java index 5e53224cb8da..02d58b107b5d 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptor.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.CONTENT_LENGTH_HEADER; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.CHECKSUM; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.getObjectChecksumEnabledPerResponse; -import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.putObjectChecksumEnabled; +import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.responseChecksumIsValid; +import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.shouldRecordChecksum; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.validatePutObjectChecksum; import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; @@ -29,6 +30,7 @@ import software.amazon.awssdk.core.checksums.Md5Checksum; import software.amazon.awssdk.core.checksums.SdkChecksum; import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttribute; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; import software.amazon.awssdk.core.sync.RequestBody; @@ -39,15 +41,16 @@ @SdkInternalApi public final class SyncChecksumValidationInterceptor implements ExecutionInterceptor { + private static ExecutionAttribute SYNC_RECORDING_CHECKSUM = new ExecutionAttribute<>("syncRecordingChecksum"); @Override public Optional modifyHttpContent(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) { - - if (putObjectChecksumEnabled(context.request(), SYNC, executionAttributes, context.httpRequest()) + if (shouldRecordChecksum(context.request(), SYNC, executionAttributes, context.httpRequest()) && context.requestBody().isPresent()) { SdkChecksum checksum = new Md5Checksum(); executionAttributes.putAttribute(CHECKSUM, checksum); + executionAttributes.putAttribute(SYNC_RECORDING_CHECKSUM, true); RequestBody requestBody = context.requestBody().get(); @@ -65,7 +68,6 @@ public Optional modifyHttpContent(Context.ModifyHttpRequest context @Override public Optional modifyHttpResponseContent(Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes) { - if (getObjectChecksumEnabledPerResponse(context.request(), context.httpResponse()) && context.responseBody().isPresent()) { @@ -86,7 +88,10 @@ public Optional modifyHttpResponseContent(Context.ModifyHttpRespons @Override public void afterUnmarshalling(Context.AfterUnmarshalling context, ExecutionAttributes executionAttributes) { - if (putObjectChecksumEnabled(context.request(), SYNC, executionAttributes, context.httpResponse())) { + boolean recordingChecksum = Boolean.TRUE.equals(executionAttributes.getAttribute(SYNC_RECORDING_CHECKSUM)); + boolean responseChecksumIsValid = responseChecksumIsValid(context.httpResponse()); + + if (recordingChecksum && responseChecksumIsValid) { validatePutObjectChecksum((PutObjectResponse) context.response(), executionAttributes); } } diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultS3Presigner.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultS3Presigner.java index a8c12ebe4a6b..6df831bbbcc0 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultS3Presigner.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultS3Presigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -64,11 +64,31 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.protocols.xml.AwsS3ProtocolFactory; import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.services.s3.presigner.model.AbortMultipartUploadPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.CompleteMultipartUploadPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.CreateMultipartUploadPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.UploadPartPresignRequest; +import software.amazon.awssdk.services.s3.transform.AbortMultipartUploadRequestMarshaller; +import software.amazon.awssdk.services.s3.transform.CompleteMultipartUploadRequestMarshaller; +import software.amazon.awssdk.services.s3.transform.CreateMultipartUploadRequestMarshaller; import software.amazon.awssdk.services.s3.transform.GetObjectRequestMarshaller; +import software.amazon.awssdk.services.s3.transform.PutObjectRequestMarshaller; +import software.amazon.awssdk.services.s3.transform.UploadPartRequestMarshaller; import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.Validate; @@ -83,11 +103,39 @@ public final class DefaultS3Presigner extends DefaultSdkPresigner implements S3P private final List clientInterceptors; private final GetObjectRequestMarshaller getObjectRequestMarshaller; + private final PutObjectRequestMarshaller putObjectRequestMarshaller; + private final CreateMultipartUploadRequestMarshaller createMultipartUploadRequestMarshaller; + private final UploadPartRequestMarshaller uploadPartRequestMarshaller; + private final CompleteMultipartUploadRequestMarshaller completeMultipartUploadRequestMarshaller; + private final AbortMultipartUploadRequestMarshaller abortMultipartUploadRequestMarshaller; private DefaultS3Presigner(Builder b) { super(b); + this.clientInterceptors = initializeInterceptors(); - this.getObjectRequestMarshaller = initializeGetObjectRequestMarshaller(); + + // Copied from DefaultS3Client#init + AwsS3ProtocolFactory protocolFactory = AwsS3ProtocolFactory.builder() + .clientConfiguration(createClientConfiguration()) + .build(); + + // Copied from DefaultS3Client#getObject + this.getObjectRequestMarshaller = new GetObjectRequestMarshaller(protocolFactory); + + // Copied from DefaultS3Client#putObject + this.putObjectRequestMarshaller = new PutObjectRequestMarshaller(protocolFactory); + + // Copied from DefaultS3Client#createMultipartUpload + this.createMultipartUploadRequestMarshaller = new CreateMultipartUploadRequestMarshaller(protocolFactory); + + // Copied from DefaultS3Client#uploadPart + this.uploadPartRequestMarshaller = new UploadPartRequestMarshaller(protocolFactory); + + // Copied from DefaultS3Client#completeMultipartUpload + this.completeMultipartUploadRequestMarshaller = new CompleteMultipartUploadRequestMarshaller(protocolFactory); + + // Copied from DefaultS3Client#abortMultipartUpload + this.abortMultipartUploadRequestMarshaller = new AbortMultipartUploadRequestMarshaller(protocolFactory); } public static S3Presigner.Builder builder() { @@ -104,35 +152,22 @@ private List initializeInterceptors() { return mergeLists(interceptorFactory.getGlobalInterceptors(), s3Interceptors); } - /** - * Copied from {@code DefaultS3Client}. - */ - private GetObjectRequestMarshaller initializeGetObjectRequestMarshaller() { - // Copied from DefaultS3Client#init - AwsS3ProtocolFactory protocolFactory = AwsS3ProtocolFactory.builder() - .clientConfiguration(createClientConfiguration()) - .build(); - // Copied from DefaultS3Client#getObject - return new GetObjectRequestMarshaller(protocolFactory); - } - /** * Copied from {@link AwsDefaultClientBuilder}. */ private SdkClientConfiguration createClientConfiguration() { - return SdkClientConfiguration.builder() - .option(SdkClientOption.ENDPOINT, resolveEndpoint()) - .build(); - } - - private URI resolveEndpoint() { if (endpointOverride() != null) { - return endpointOverride(); + return SdkClientConfiguration.builder() + .option(SdkClientOption.ENDPOINT, endpointOverride()) + .option(SdkClientOption.ENDPOINT_OVERRIDDEN, true) + .build(); + } else { + URI defaultEndpoint = new DefaultServiceEndpointBuilder(SERVICE_NAME, "https").withRegion(region()) + .getServiceEndpoint(); + return SdkClientConfiguration.builder() + .option(SdkClientOption.ENDPOINT, defaultEndpoint) + .build(); } - - return new DefaultServiceEndpointBuilder(SERVICE_NAME, "https") - .withRegion(region()) - .getServiceEndpoint(); } @Override @@ -146,6 +181,61 @@ public PresignedGetObjectRequest presignGetObject(GetObjectPresignRequest reques .build(); } + @Override + public PresignedPutObjectRequest presignPutObject(PutObjectPresignRequest request) { + return presign(PresignedPutObjectRequest.builder(), + request, + request.putObjectRequest(), + PutObjectRequest.class, + putObjectRequestMarshaller::marshall, + "PutObject") + .build(); + } + + @Override + public PresignedCreateMultipartUploadRequest presignCreateMultipartUpload(CreateMultipartUploadPresignRequest request) { + return presign(PresignedCreateMultipartUploadRequest.builder(), + request, + request.createMultipartUploadRequest(), + CreateMultipartUploadRequest.class, + createMultipartUploadRequestMarshaller::marshall, + "CreateMultipartUpload") + .build(); + } + + @Override + public PresignedUploadPartRequest presignUploadPart(UploadPartPresignRequest request) { + return presign(PresignedUploadPartRequest.builder(), + request, + request.uploadPartRequest(), + UploadPartRequest.class, + uploadPartRequestMarshaller::marshall, + "UploadPart") + .build(); + } + + @Override + public PresignedCompleteMultipartUploadRequest presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest request) { + return presign(PresignedCompleteMultipartUploadRequest.builder(), + request, + request.completeMultipartUploadRequest(), + CompleteMultipartUploadRequest.class, + completeMultipartUploadRequestMarshaller::marshall, + "CompleteMultipartUpload") + .build(); + } + + @Override + public PresignedAbortMultipartUploadRequest presignAbortMultipartUpload(AbortMultipartUploadPresignRequest request) { + return presign(PresignedAbortMultipartUploadRequest.builder(), + request, + request.abortMultipartUploadRequest(), + AbortMultipartUploadRequest.class, + abortMultipartUploadRequestMarshaller::marshall, + "AbortMultipartUpload") + .build(); + } + /** * Generate a {@link PresignedRequest} from a {@link PresignedRequest} and {@link SdkRequest}. */ @@ -374,7 +464,8 @@ private void initializePresignedRequest(PresignedRequest.Builder presignedReques public static final class Builder extends DefaultSdkPresigner.Builder implements S3Presigner.Builder { - private Builder() {} + private Builder() { + } @Override public S3Presigner build() { diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultSdkPresigner.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultSdkPresigner.java index 5104b472cd44..dcf94b6375be 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultSdkPresigner.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/presigner/DefaultSdkPresigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -77,7 +77,8 @@ public abstract static class Builder> private AwsCredentialsProvider credentialsProvider; private URI endpointOverride; - protected Builder() {} + protected Builder() { + } @Override public B region(Region region) { diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/ArnConverter.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/ArnConverter.java new file mode 100644 index 000000000000..6a6ecd931b1f --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/ArnConverter.java @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.arns.Arn; + +/** + * An interface for converting an AWS ARN into a service specific {@link AwsResource}. Services that model + * their own AWS resources will provide a specific implementation of this ARN parser. + *

    + * @param The service specific representation of {@link AwsResource}. + */ +@SdkInternalApi +@FunctionalInterface +public interface ArnConverter { + /** + * Converts an AWS ARN into a service specific {@link AwsResource}. + * + * @param arn The ARN to convert. + * @return A service specific {@link AwsResource}. + */ + T convertArn(Arn arn); +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/AwsResource.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/AwsResource.java new file mode 100644 index 000000000000..8821b383e365 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/AwsResource.java @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * An abstract representation of an AWS Resource. Provides an interface to properties that are common across all AWS + * resource types. Services may provide concrete implementations that can be found in each service module. + */ +@SdkInternalApi +public interface AwsResource { + /** + * Gets the partition associated with the AWS Resource (e.g.: 'aws') if one has been specified. + * @return the optional value for the partition. + */ + Optional partition(); + + /** + * Gets the region associated with the AWS Resource (e.g.: 'us-east-1') if one has been specified. + * @return the optional value for the region. + */ + Optional region(); + + /** + * Gets the account ID associated with the AWS Resource if one has been specified. + * @return the optional value for the account ID. + */ + Optional accountId(); +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointBuilder.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointBuilder.java new file mode 100644 index 000000000000..be2ddd2b1f42 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointBuilder.java @@ -0,0 +1,128 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import static software.amazon.awssdk.utils.http.SdkHttpUtils.urlEncode; + +import java.net.URI; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * This class is used to construct an endpoint host for an S3 access point. + */ +@SdkInternalApi +public class S3AccessPointBuilder { + private static final Pattern HOSTNAME_COMPLIANT_PATTERN = Pattern.compile("[A-Za-z0-9\\-]+"); + private static final int HOSTNAME_MAX_LENGTH = 63; + + private Boolean dualstackEnabled; + private String accessPointName; + private String region; + private String accountId; + private String protocol; + private String domain; + + /** + * Create a new instance of this builder class. + */ + public static S3AccessPointBuilder create() { + return new S3AccessPointBuilder(); + } + + /** + * Enable DualStack endpoint. + */ + public S3AccessPointBuilder dualstackEnabled(Boolean dualstackEnabled) { + this.dualstackEnabled = dualstackEnabled; + return this; + } + + /** + * The S3 Access Point name. + */ + public S3AccessPointBuilder accessPointName(String accessPointName) { + this.accessPointName = accessPointName; + return this; + } + + /** + * The AWS region hosting the Access Point. + */ + public S3AccessPointBuilder region(String region) { + this.region = region; + return this; + } + + /** + * The ID of the AWS Account the Access Point is associated with. + */ + public S3AccessPointBuilder accountId(String accountId) { + this.accountId = accountId; + return this; + } + + /** + * The protocol to be used with the endpoint URI. + */ + public S3AccessPointBuilder protocol(String protocol) { + this.protocol = protocol; + return this; + } + + /** + * The TLD for the access point. + */ + public S3AccessPointBuilder domain(String domain) { + this.domain = domain; + return this; + } + + /** + * Generate an endpoint URI with no path that maps to the Access Point information stored in this builder. + */ + public URI toUri() { + validateHostnameCompliant(accountId, "accountId"); + validateHostnameCompliant(accessPointName, "accessPointName"); + + String dualStackSegment = Boolean.TRUE.equals(dualstackEnabled) ? ".dualstack" : ""; + String uriString = String.format("%s://%s-%s.s3-accesspoint%s.%s.%s", protocol, urlEncode(accessPointName), + accountId, dualStackSegment, region, domain); + return URI.create(uriString); + } + + private static void validateHostnameCompliant(String hostnameComponent, String paramName) { + if (hostnameComponent.isEmpty()) { + throw new IllegalArgumentException( + String.format("An S3 Access Point ARN has been passed that is not valid: the required '%s' " + + "component is missing.", paramName)); + } + + if (hostnameComponent.length() > HOSTNAME_MAX_LENGTH) { + throw new IllegalArgumentException( + String.format("An S3 Access Point ARN has been passed that is not valid: the '%s' " + + "component exceeds the maximum length of %d characters.", paramName, HOSTNAME_MAX_LENGTH)); + } + + Matcher m = HOSTNAME_COMPLIANT_PATTERN.matcher(hostnameComponent); + if (!m.matches()) { + throw new IllegalArgumentException( + String.format("An S3 Access Point ARN has been passed that is not valid: the '%s' " + + "component must only contain alphanumeric characters and dashes.", paramName)); + } + } +} \ No newline at end of file diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointResource.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointResource.java new file mode 100644 index 000000000000..2f7977afa824 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointResource.java @@ -0,0 +1,206 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * An {@link S3Resource} that represents an S3 access point. + */ +@SdkInternalApi +public final class S3AccessPointResource + implements S3Resource, ToCopyableBuilder { + + private static final S3ResourceType S3_RESOURCE_TYPE = S3ResourceType.ACCESS_POINT; + + private final String partition; + private final String region; + private final String accountId; + private final String accessPointName; + + private S3AccessPointResource(Builder b) { + this.accessPointName = Validate.paramNotBlank(b.accessPointName, "accessPointName"); + this.partition = Validate.paramNotBlank(b.partition, "partition"); + this.region = Validate.paramNotBlank(b.region, "region"); + this.accountId = Validate.paramNotBlank(b.accountId, "accountId"); + } + + /** + * Get a new builder for this class. + * @return A newly initialized instance of a builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Gets the resource type for this access point. + * @return This will always return "access_point". + */ + @Override + public String type() { + return S3_RESOURCE_TYPE.toString(); + } + + /** + * Gets the AWS partition name associated with this access point (e.g.: 'aws'). + * @return the name of the partition. + */ + @Override + public Optional partition() { + return Optional.ofNullable(this.partition); + } + + /** + * Gets the AWS region name associated with this bucket (e.g.: 'us-east-1'). + * @return the name of the region. + */ + @Override + public Optional region() { + return Optional.ofNullable(this.region); + } + + /** + * Gets the AWS account ID associated with this bucket. + * @return the AWS account ID. + */ + @Override + public Optional accountId() { + return Optional.ofNullable(this.accountId); + } + + /** + * Gets the name of the access point. + * @return the name of the access point. + */ + public String accessPointName() { + return this.accessPointName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + S3AccessPointResource that = (S3AccessPointResource) o; + + if (partition != null ? ! partition.equals(that.partition) : that.partition != null) { + return false; + } + if (region != null ? ! region.equals(that.region) : that.region != null) { + return false; + } + if (accountId != null ? ! accountId.equals(that.accountId) : that.accountId != null) { + return false; + } + return accessPointName.equals(that.accessPointName); + } + + @Override + public int hashCode() { + int result = partition != null ? partition.hashCode() : 0; + result = 31 * result + (region != null ? region.hashCode() : 0); + result = 31 * result + (accountId != null ? accountId.hashCode() : 0); + result = 31 * result + accessPointName.hashCode(); + return result; + } + + @Override + public Builder toBuilder() { + return builder() + .partition(partition) + .region(region) + .accountId(accountId) + .accessPointName(accessPointName); + } + + /** + * A builder for {@link S3AccessPointResource} objects. + */ + public static final class Builder implements CopyableBuilder { + private String partition; + private String region; + private String accountId; + private String accessPointName; + + private Builder() { + } + + public void setPartition(String partition) { + partition(partition); + } + + /** + * The AWS partition associated with the access point. + */ + public Builder partition(String partition) { + this.partition = partition; + return this; + } + + public void setRegion(String region) { + region(region); + } + + /** + * The AWS region associated with the access point. + */ + public Builder region(String region) { + this.region = region; + return this; + } + + public void setAccountId(String accountId) { + accountId(accountId); + } + + /** + * The AWS account ID associated with the access point. + */ + public Builder accountId(String accountId) { + this.accountId = accountId; + return this; + } + + public void setAccessPointName(String accessPointName) { + accessPointName(accessPointName); + } + + /** + * The name of the S3 access point. + */ + public Builder accessPointName(String accessPointName) { + this.accessPointName = accessPointName; + return this; + } + + /** + * Builds an instance of {@link S3AccessPointResource}. + */ + @Override + public S3AccessPointResource build() { + return new S3AccessPointResource(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ArnConverter.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ArnConverter.java new file mode 100644 index 000000000000..47f79d25c644 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ArnConverter.java @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.arns.Arn; + +/** + * An implementation of {@link ArnConverter} that can be used to convert valid {@link Arn} representations of s3 + * resources into {@link S3Resource} objects. To fetch an instance of this class, use the singleton getter method + * {@link #create()}. + */ +@SdkInternalApi +public final class S3ArnConverter implements ArnConverter { + private static final S3ArnConverter INSTANCE = new S3ArnConverter(); + + private S3ArnConverter() { + } + + /** + * Gets a static singleton instance of an {@link S3ArnConverter}. + * @return A static instance of an {@link S3ArnConverter}. + */ + public static S3ArnConverter create() { + return INSTANCE; + } + + /** + * Converts a valid ARN representation of an S3 resource into a {@link S3Resource} object. + * @param arn The ARN to convert. + * @return An {@link S3Resource} object as specified by the ARN. + * @throws IllegalArgumentException if the ARN is not a valid representation of an S3 resource supported by this + * SDK. + */ + @Override + public S3Resource convertArn(Arn arn) { + Arn v2Arn = convertToV2Arn(arn); + S3ResourceType s3ResourceType; + + if (!v2Arn.resource().resourceType().isPresent()) { + throw new IllegalArgumentException("Unknown ARN type"); + } + + try { + s3ResourceType = + S3ResourceType.fromValue(v2Arn.resource().resourceType().get()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Unknown ARN type '" + v2Arn.resource().resourceType().get() + "'"); + } + + switch (s3ResourceType) { + case OBJECT: + return parseS3ObjectArn(v2Arn); + case ACCESS_POINT: + return parseS3AccessPointArn(v2Arn); + case BUCKET: + return parseS3BucketArn(v2Arn); + default: + throw new IllegalArgumentException("Unknown ARN type '" + s3ResourceType + "'"); + } + } + + private Arn convertToV2Arn(Arn arn) { + if (!isV1Arn(arn)) { + return arn; + } + + String resource = arn.resourceAsString(); + + if (resource.contains("/")) { + return arn.toBuilder().resource("object:" + arn.resourceAsString()).build(); + } else { + return arn.toBuilder().resource("bucket_name:" + arn.resourceAsString()).build(); + } + } + + private S3BucketResource parseS3BucketArn(Arn arn) { + return S3BucketResource.builder() + .partition(arn.partition()) + .region(arn.region().orElse(null)) + .accountId(arn.accountId().orElse(null)) + .bucketName(arn.resource().resource()) + .build(); + } + + private S3AccessPointResource parseS3AccessPointArn(Arn arn) { + return S3AccessPointResource.builder() + .partition(arn.partition()) + .region(arn.region().orElse(null)) + .accountId(arn.accountId().orElse(null)) + .accessPointName(arn.resource().resource()) + .build(); + } + + private S3ObjectResource parseS3ObjectArn(Arn arn) { + String resourceString = arn.resource().resource(); + String [] splitResourceString = resourceString.split("/"); + + if (splitResourceString.length < 2) { + throw new IllegalArgumentException("Invalid format for S3 object resource ARN"); + } + + String bucketName = splitResourceString[0]; + String key = splitResourceString[1]; + + return S3ObjectResource.builder() + .partition(arn.partition()) + .region(arn.region().orElse(null)) + .accountId(arn.accountId().orElse(null)) + .bucketName(bucketName) + .key(key) + .build(); + } + + private boolean isV1Arn(Arn arn) { + return !arn.accountId().isPresent() && !arn.region().isPresent(); + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3BucketResource.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3BucketResource.java new file mode 100644 index 000000000000..92a00129fd45 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3BucketResource.java @@ -0,0 +1,207 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * An {@link S3Resource} that represents an S3 bucket. + */ +@SdkInternalApi +public final class S3BucketResource + implements S3Resource, ToCopyableBuilder { + + private static final S3ResourceType S3_RESOURCE_TYPE = S3ResourceType.BUCKET; + + private final String partition; + private final String region; + private final String accountId; + private final String bucketName; + + private S3BucketResource(Builder b) { + this.bucketName = Validate.paramNotBlank(b.bucketName, "bucketName"); + this.partition = b.partition; + this.region = b.region; + this.accountId = b.accountId; + } + + /** + * Get a new builder for this class. + * @return A newly initialized instance of a builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Gets the resource type for this bucket. + * @return This will always return "bucket_name". + */ + @Override + public String type() { + return S3_RESOURCE_TYPE.toString(); + } + + /** + * Gets the AWS partition name associated with this bucket (e.g.: 'aws') if one has been specified. + * @return the optional name of the partition or empty if it has not been specified. + */ + @Override + public Optional partition() { + return Optional.ofNullable(this.partition); + } + + /** + * Gets the AWS region name associated with this bucket (e.g.: 'us-east-1') if one has been specified. + * @return the optional name of the region or empty if the region has not been specified (e.g. the resource is in + * the global namespace). + */ + @Override + public Optional region() { + return Optional.ofNullable(this.region); + } + + /** + * Gets the AWS account ID associated with this bucket if one has been specified. + * @return the optional AWS account ID or empty if the account ID has not been specified. + */ + @Override + public Optional accountId() { + return Optional.ofNullable(this.accountId); + } + + /** + * Gets the name of the bucket. + * @return the name of the bucket. + */ + public String bucketName() { + return this.bucketName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + S3BucketResource that = (S3BucketResource) o; + + if (partition != null ? ! partition.equals(that.partition) : that.partition != null) { + return false; + } + if (region != null ? ! region.equals(that.region) : that.region != null) { + return false; + } + if (accountId != null ? ! accountId.equals(that.accountId) : that.accountId != null) { + return false; + } + return bucketName.equals(that.bucketName); + } + + @Override + public int hashCode() { + int result = partition != null ? partition.hashCode() : 0; + result = 31 * result + (region != null ? region.hashCode() : 0); + result = 31 * result + (accountId != null ? accountId.hashCode() : 0); + result = 31 * result + bucketName.hashCode(); + return result; + } + + @Override + public Builder toBuilder() { + return builder() + .partition(partition) + .region(region) + .accountId(accountId) + .bucketName(bucketName); + } + + /** + * A builder for {@link S3BucketResource} objects. + */ + public static final class Builder implements CopyableBuilder { + private String partition; + private String region; + private String accountId; + private String bucketName; + + private Builder() { + } + + public void setPartition(String partition) { + partition(partition); + } + + /** + * The AWS partition associated with the bucket. + */ + public Builder partition(String partition) { + this.partition = partition; + return this; + } + + public void setRegion(String region) { + region(region); + } + + /** + * The AWS region associated with the bucket. This property is optional. + */ + public Builder region(String region) { + this.region = region; + return this; + } + + public void setAccountId(String accountId) { + accountId(accountId); + } + + /** + * The AWS account ID associated with the bucket. This property is optional. + */ + public Builder accountId(String accountId) { + this.accountId = accountId; + return this; + } + + public void setBucketName(String bucketName) { + bucketName(bucketName); + } + + /** + * The name of the S3 bucket. + */ + public Builder bucketName(String bucketName) { + this.bucketName = bucketName; + return this; + } + + /** + * Builds an instance of {@link S3BucketResource}. + */ + @Override + public S3BucketResource build() { + return new S3BucketResource(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ObjectResource.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ObjectResource.java new file mode 100644 index 000000000000..ed8763626657 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ObjectResource.java @@ -0,0 +1,235 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * An {@link S3Resource} that represents an S3 object. + */ +@SdkInternalApi +public final class S3ObjectResource + implements S3Resource, ToCopyableBuilder { + + private static final S3ResourceType S3_RESOURCE_TYPE = S3ResourceType.OBJECT; + + private final String partition; + private final String region; + private final String accountId; + private final String bucketName; + private final String key; + + private S3ObjectResource(Builder b) { + this.bucketName = Validate.paramNotBlank(b.bucketName, "bucketName"); + this.key = Validate.paramNotBlank(b.key, "key"); + this.partition = Validate.paramNotBlank(b.partition, "partition"); + this.region = b.region; + this.accountId = b.accountId; + } + + /** + * Get a new builder for this class. + * @return A newly initialized instance of a builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Gets the resource type for this S3 object. + * @return This will always return "object". + */ + @Override + public String type() { + return S3_RESOURCE_TYPE.toString(); + } + + /** + * Gets the AWS partition name associated with the S3 object (e.g.: 'aws'). + * @return the name of the partition. + */ + @Override + public Optional partition() { + return Optional.ofNullable(this.partition); + } + + /** + * Gets the AWS region name associated with the S3 object (e.g.: 'us-east-1'). + * @return the name of the region or null if the region has not been specified (e.g. the resource is in the + * global namespace). + */ + @Override + public Optional region() { + return Optional.ofNullable(this.region); + } + + /** + * Gets the AWS account ID associated with the S3 object if it has been specified. + * @return the optional AWS account ID or empty if the account ID has not been specified. + */ + @Override + public Optional accountId() { + return Optional.ofNullable(this.accountId); + } + + /** + * Gets the name of the bucket associated with the S3 object. + * @return the name of the bucket associated with the S3 object. + */ + public String bucketName() { + return this.bucketName; + } + + /** + * Gets the key of the S3 object. + * @return the key of the S3 object. + */ + public String key() { + return this.key; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + S3ObjectResource that = (S3ObjectResource) o; + + if (partition != null ? ! partition.equals(that.partition) : that.partition != null) { + return false; + } + if (region != null ? ! region.equals(that.region) : that.region != null) { + return false; + } + if (accountId != null ? ! accountId.equals(that.accountId) : that.accountId != null) { + return false; + } + if (! bucketName.equals(that.bucketName)) { + return false; + } + return key.equals(that.key); + } + + @Override + public int hashCode() { + int result = partition != null ? partition.hashCode() : 0; + result = 31 * result + (region != null ? region.hashCode() : 0); + result = 31 * result + (accountId != null ? accountId.hashCode() : 0); + result = 31 * result + bucketName.hashCode(); + result = 31 * result + key.hashCode(); + return result; + } + + @Override + public Builder toBuilder() { + return builder() + .partition(partition) + .region(region) + .accountId(accountId) + .bucketName(bucketName) + .key(key); + } + + /** + * A builder for {@link S3ObjectResource} objects. + */ + public static final class Builder implements CopyableBuilder { + private String partition; + private String region; + private String accountId; + private String bucketName; + private String key; + + private Builder() { + } + + public void setPartition(String partition) { + partition(partition); + } + + /** + * The AWS partition associated with the S3 object. + */ + public Builder partition(String partition) { + this.partition = partition; + return this; + } + + public void setRegion(String region) { + region(region); + } + + /** + * The AWS region associated with the S3 object. This property is optional. + */ + public Builder region(String region) { + this.region = region; + return this; + } + + public void setAccountId(String accountId) { + accountId(accountId); + } + + /** + * The AWS account ID associated with the S3 object. This property is optional. + */ + public Builder accountId(String accountId) { + this.accountId = accountId; + return this; + } + + public void setBucketName(String bucketName) { + bucketName(bucketName); + } + + /** + * The name of the S3 bucket associated with this S3 object. + */ + public Builder bucketName(String bucketName) { + this.bucketName = bucketName; + return this; + } + + public void setKey(String key) { + key(key); + } + + /** + * The key of the S3 object. + */ + public Builder key(String key) { + this.key = key; + return this; + } + + /** + * Builds an instance of {@link S3BucketResource}. + */ + @Override + public S3ObjectResource build() { + return new S3ObjectResource(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3Resource.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3Resource.java new file mode 100644 index 000000000000..fa6da07ca1ff --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3Resource.java @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * A representation of an AWS S3 resource. See {@link S3ResourceType} for a list and description of all valid types. + */ +@SdkInternalApi +public interface S3Resource extends AwsResource { + /** + * Gets the type of S3 resource represented by this object (e.g.: 'bucket_name'). See {@link S3ResourceType} for + * a list and description of all valid types. + * @return the string name of the S3 resource type. + */ + String type(); +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ResourceType.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ResourceType.java new file mode 100644 index 000000000000..79b4ed673d2b --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/resource/S3ResourceType.java @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * An enum representing the types of resources supported by S3. Each resource type below will have a + * concrete implementation of {@link S3Resource}. + */ +@SdkInternalApi +public enum S3ResourceType { + /** + * A specific S3 bucket. Implemented by {@link S3BucketResource}. + */ + BUCKET("bucket_name"), + /** + * An access point that fronts a bucket. Implemented by {@link S3AccessPointResource}. + */ + ACCESS_POINT("accesspoint"), + /** + * A specific S3 object (bucket and key). Implemented by {@link S3ObjectResource}. + */ + OBJECT("object"); + + private final String value; + + S3ResourceType(String value) { + this.value = value; + } + + /** + * @return The canonical string value of this resource type. + */ + @Override + public String toString() { + return value; + } + + /** + * Use this in place of valueOf. + * + * @param value real value + * @return S3ResourceType corresponding to the value + * @throws IllegalArgumentException If the specified value does not map to one of the known values in this enum. + */ + public static S3ResourceType fromValue(String value) { + if (value == null || "".equals(value)) { + throw new IllegalArgumentException("Value cannot be null or empty!"); + } + + for (S3ResourceType enumEntry : S3ResourceType.values()) { + if (enumEntry.toString().equals(value)) { + return enumEntry; + } + } + + throw new IllegalArgumentException("Cannot create enum from " + value + " value!"); + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/ProfileUseArnRegionProvider.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/ProfileUseArnRegionProvider.java new file mode 100644 index 000000000000..e3703abd9aa2 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/ProfileUseArnRegionProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import java.util.Optional; +import java.util.function.Supplier; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.utils.StringUtils; + +/** + * Loads useArnRegion configuration from the {@link ProfileFile#defaultProfileFile()} using the default profile name. + */ +@SdkInternalApi +public final class ProfileUseArnRegionProvider implements UseArnRegionProvider { + /** + * Property name for specifying whether or not use arn region should be enabled. + */ + private static final String AWS_USE_ARN_REGION = "s3_use_arn_region"; + + private final Supplier profileFile; + private final String profileName; + + private ProfileUseArnRegionProvider(Supplier profileFile, String profileName) { + this.profileFile = profileFile; + this.profileName = profileName; + } + + public static ProfileUseArnRegionProvider create() { + return new ProfileUseArnRegionProvider(ProfileFile::defaultProfileFile, + ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow()); + } + + public static ProfileUseArnRegionProvider create(ProfileFile profileFile, String profileName) { + return new ProfileUseArnRegionProvider(() -> profileFile, profileName); + } + + @Override + public Optional resolveUseArnRegion() { + return profileFile.get() + .profile(profileName) + .map(p -> p.properties().get(AWS_USE_ARN_REGION)) + .map(StringUtils::safeStringToBoolean); + } +} + diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/SystemsSettingsUseArnRegionProvider.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/SystemsSettingsUseArnRegionProvider.java new file mode 100644 index 000000000000..efbf213e7158 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/SystemsSettingsUseArnRegionProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.services.s3.S3SystemSetting; + +/** + * {@link UseArnRegionProvider} implementation that loads userArnRegion configuration from system properties + * and environment variables. + */ +@SdkInternalApi +public final class SystemsSettingsUseArnRegionProvider implements UseArnRegionProvider { + + private SystemsSettingsUseArnRegionProvider() { + } + + public static SystemsSettingsUseArnRegionProvider create() { + return new SystemsSettingsUseArnRegionProvider(); + } + + @Override + public Optional resolveUseArnRegion() { + return S3SystemSetting.AWS_S3_USE_ARN_REGION.getBooleanValue(); + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProvider.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProvider.java new file mode 100644 index 000000000000..b23d32113bb4 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; + +/** + * Interface for loading useArnRegion configuration. + */ +@FunctionalInterface +@SdkInternalApi +public interface UseArnRegionProvider { + + /** + * @return whether use-arn-region is enabled, or empty if it is not configured. + */ + Optional resolveUseArnRegion(); +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProviderChain.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProviderChain.java new file mode 100644 index 000000000000..9149152b2971 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProviderChain.java @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.profiles.ProfileFileSystemSetting; +import software.amazon.awssdk.utils.Logger; + +/** + * {@link UseArnRegionProvider} implementation that chains together multiple useArnRegion providers. + */ +@SdkInternalApi +public final class UseArnRegionProviderChain implements UseArnRegionProvider { + private static final Logger log = Logger.loggerFor(UseArnRegionProvider.class); + + private final List providers; + + private UseArnRegionProviderChain(List providers) { + this.providers = providers; + } + + /** + * Creates a default {@link UseArnRegionProviderChain}. + * + *

    + * AWS use arn region provider that looks for the useArnRegion in this order: + * + *

      + *
    1. Check the 'aws.useArnRegion' system property for the region.
    2. + *
    3. Check the 'AWS_USE_ARN_REGION' environment variable for the region.
    4. + *
    + */ + public static UseArnRegionProviderChain create() { + return create(ProfileFile.defaultProfileFile(), + ProfileFileSystemSetting.AWS_PROFILE.getStringValueOrThrow()); + } + + public static UseArnRegionProviderChain create(ProfileFile profileFile, String profileName) { + return new UseArnRegionProviderChain(Arrays.asList(SystemsSettingsUseArnRegionProvider.create(), + ProfileUseArnRegionProvider.create(profileFile, profileName))); + } + + @Override + public Optional resolveUseArnRegion() { + for (UseArnRegionProvider provider : providers) { + try { + Optional useArnRegion = provider.resolveUseArnRegion(); + if (useArnRegion.isPresent()) { + return useArnRegion; + } + } catch (Exception ex) { + log.warn(() -> "Failed to retrieve useArnRegion from " + provider); + } + } + return Optional.empty(); + } +} \ No newline at end of file diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/model/GetUrlRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/model/GetUrlRequest.java index 104ffbc91343..095e97ac2962 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/model/GetUrlRequest.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/model/GetUrlRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -42,14 +42,14 @@ @SdkPublicApi public final class GetUrlRequest implements SdkPojo, ToCopyableBuilder { private static final SdkField BUCKET_FIELD = SdkField - .builder(MarshallingType.STRING) + .builder(MarshallingType.STRING) .getter(getter(GetUrlRequest::bucket)) .setter(setter(Builder::bucket)) .traits(LocationTrait.builder().location(MarshallLocation.PATH).locationName("Bucket") .unmarshallLocationName("Bucket").build()).build(); private static final SdkField KEY_FIELD = SdkField - .builder(MarshallingType.STRING) + .builder(MarshallingType.STRING) .getter(getter(GetUrlRequest::key)) .setter(setter(Builder::key)) .traits(LocationTrait.builder().location(MarshallLocation.GREEDY_PATH).locationName("Key") diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/S3Presigner.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/S3Presigner.java index 6ba05e732119..a2a3510d632c 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/S3Presigner.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/S3Presigner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -31,9 +31,24 @@ import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain; import software.amazon.awssdk.services.s3.internal.presigner.DefaultS3Presigner; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; import software.amazon.awssdk.services.s3.model.GetObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.presigner.model.AbortMultipartUploadPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.CompleteMultipartUploadPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.CreateMultipartUploadPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedAbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedCompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedCreateMultipartUploadRequest; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedUploadPartRequest; +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; +import software.amazon.awssdk.services.s3.presigner.model.UploadPartPresignRequest; /** * Enables signing an S3 {@link SdkRequest} so that it can be executed without requiring any additional authentication on the @@ -263,6 +278,234 @@ default PresignedGetObjectRequest presignGetObject(Consumer + * + * Example Usage + *

    + * + *

    +     * {@code
    +     *     S3Presigner presigner = ...;
    +     *
    +     *     // Create a PutObjectRequest to be pre-signed
    +     *     PutObjectRequest putObjectRequest = ...;
    +     *
    +     *     // Create a PutObjectPresignRequest to specify the signature duration
    +     *     PutObjectPresignRequest putObjectPresignRequest =
    +     *         PutObjectPresignRequest.builder()
    +     *                                .signatureDuration(Duration.ofMinutes(10))
    +     *                                .putObjectRequest(request)
    +     *                                .build();
    +     *
    +     *     // Generate the presigned request
    +     *     PresignedPutObjectRequest presignedPutObjectRequest =
    +     *         presigner.presignPutObject(putObjectPresignRequest);
    +     * }
    +     * 
    + */ + PresignedPutObjectRequest presignPutObject(PutObjectPresignRequest request); + + /** + * Presign a {@link PutObjectRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * This is a shorter method of invoking {@link #presignPutObject(PutObjectPresignRequest)} without needing + * to call {@code PutObjectPresignRequest.builder()} or {@code .build()}. + * + * @see #presignPutObject(PutObjectPresignRequest) + */ + default PresignedPutObjectRequest presignPutObject(Consumer request) { + PutObjectPresignRequest.Builder builder = PutObjectPresignRequest.builder(); + request.accept(builder); + return presignPutObject(builder.build()); + } + + /** + * Presign a {@link CreateMultipartUploadRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * + * Example Usage + *

    + * + *

    +     * {@code
    +     *     S3Presigner presigner = ...;
    +     *
    +     *     // Create a CreateMultipartUploadRequest to be pre-signed
    +     *     CreateMultipartUploadRequest createMultipartUploadRequest = ...;
    +     *
    +     *     // Create a CreateMultipartUploadPresignRequest to specify the signature duration
    +     *     CreateMultipartUploadPresignRequest createMultipartUploadPresignRequest =
    +     *         CreateMultipartUploadPresignRequest.builder()
    +     *                                            .signatureDuration(Duration.ofMinutes(10))
    +     *                                            .createMultipartUploadRequest(request)
    +     *                                            .build();
    +     *
    +     *     // Generate the presigned request
    +     *     PresignedCreateMultipartUploadRequest presignedCreateMultipartUploadRequest =
    +     *         presigner.presignCreateMultipartUpload(createMultipartUploadPresignRequest);
    +     * }
    +     * 
    + */ + PresignedCreateMultipartUploadRequest presignCreateMultipartUpload(CreateMultipartUploadPresignRequest request); + + /** + * Presign a {@link CreateMultipartUploadRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * This is a shorter method of invoking {@link #presignCreateMultipartUpload(CreateMultipartUploadPresignRequest)} without + * needing to call {@code CreateMultipartUploadPresignRequest.builder()} or {@code .build()}. + * + * @see #presignCreateMultipartUpload(CreateMultipartUploadPresignRequest) + */ + default PresignedCreateMultipartUploadRequest presignCreateMultipartUpload( + Consumer request) { + CreateMultipartUploadPresignRequest.Builder builder = CreateMultipartUploadPresignRequest.builder(); + request.accept(builder); + return presignCreateMultipartUpload(builder.build()); + } + + /** + * Presign a {@link UploadPartRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * + * Example Usage + *

    + * + *

    +     * {@code
    +     *     S3Presigner presigner = ...;
    +     *
    +     *     // Create a UploadPartRequest to be pre-signed
    +     *     UploadPartRequest uploadPartRequest = ...;
    +     *
    +     *     // Create a UploadPartPresignRequest to specify the signature duration
    +     *     UploadPartPresignRequest uploadPartPresignRequest =
    +     *         UploadPartPresignRequest.builder()
    +     *                                 .signatureDuration(Duration.ofMinutes(10))
    +     *                                 .uploadPartRequest(request)
    +     *                                 .build();
    +     *
    +     *     // Generate the presigned request
    +     *     PresignedUploadPartRequest presignedUploadPartRequest =
    +     *         presigner.presignUploadPart(uploadPartPresignRequest);
    +     * }
    +     * 
    + */ + PresignedUploadPartRequest presignUploadPart(UploadPartPresignRequest request); + + /** + * Presign a {@link UploadPartRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * This is a shorter method of invoking {@link #presignUploadPart(UploadPartPresignRequest)} without needing + * to call {@code UploadPartPresignRequest.builder()} or {@code .build()}. + * + * @see #presignUploadPart(UploadPartPresignRequest) + */ + default PresignedUploadPartRequest presignUploadPart(Consumer request) { + UploadPartPresignRequest.Builder builder = UploadPartPresignRequest.builder(); + request.accept(builder); + return presignUploadPart(builder.build()); + } + + /** + * Presign a {@link CompleteMultipartUploadRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * + * Example Usage + *

    + * + *

    +     * {@code
    +     *     S3Presigner presigner = ...;
    +     *
    +     *     // Complete a CompleteMultipartUploadRequest to be pre-signed
    +     *     CompleteMultipartUploadRequest completeMultipartUploadRequest = ...;
    +     *
    +     *     // Create a CompleteMultipartUploadPresignRequest to specify the signature duration
    +     *     CompleteMultipartUploadPresignRequest completeMultipartUploadPresignRequest =
    +     *         CompleteMultipartUploadPresignRequest.builder()
    +     *                                              .signatureDuration(Duration.ofMinutes(10))
    +     *                                              .completeMultipartUploadRequest(request)
    +     *                                              .build();
    +     *
    +     *     // Generate the presigned request
    +     *     PresignedCompleteMultipartUploadRequest presignedCompleteMultipartUploadRequest =
    +     *         presigner.presignCompleteMultipartUpload(completeMultipartUploadPresignRequest);
    +     * }
    +     * 
    + */ + PresignedCompleteMultipartUploadRequest presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest request); + + /** + * Presign a {@link CompleteMultipartUploadRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * This is a shorter method of invoking {@link #presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest)} without + * needing to call {@code CompleteMultipartUploadPresignRequest.builder()} or {@code .build()}. + * + * @see #presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest) + */ + default PresignedCompleteMultipartUploadRequest presignCompleteMultipartUpload( + Consumer request) { + CompleteMultipartUploadPresignRequest.Builder builder = CompleteMultipartUploadPresignRequest.builder(); + request.accept(builder); + return presignCompleteMultipartUpload(builder.build()); + } + + /** + * Presign a {@link AbortMultipartUploadRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * + * Example Usage + *

    + * + *

    +     * {@code
    +     *     S3Presigner presigner = ...;
    +     *
    +     *     // Complete a AbortMultipartUploadRequest to be pre-signed
    +     *     AbortMultipartUploadRequest abortMultipartUploadRequest = ...;
    +     *
    +     *     // Create a AbortMultipartUploadPresignRequest to specify the signature duration
    +     *     AbortMultipartUploadPresignRequest abortMultipartUploadPresignRequest =
    +     *         AbortMultipartUploadPresignRequest.builder()
    +     *                                              .signatureDuration(Duration.ofMinutes(10))
    +     *                                              .abortMultipartUploadRequest(request)
    +     *                                              .build();
    +     *
    +     *     // Generate the presigned request
    +     *     PresignedAbortMultipartUploadRequest presignedAbortMultipartUploadRequest =
    +     *         presigner.presignAbortMultipartUpload(abortMultipartUploadPresignRequest);
    +     * }
    +     * 
    + */ + PresignedAbortMultipartUploadRequest presignAbortMultipartUpload(AbortMultipartUploadPresignRequest request); + + /** + * Presign a {@link AbortMultipartUploadRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + *

    + * This is a shorter method of invoking {@link #presignAbortMultipartUpload(AbortMultipartUploadPresignRequest)} without + * needing to call {@code AbortMultipartUploadPresignRequest.builder()} or {@code .build()}. + * + * @see #presignAbortMultipartUpload(AbortMultipartUploadPresignRequest) + */ + default PresignedAbortMultipartUploadRequest presignAbortMultipartUpload( + Consumer request) { + AbortMultipartUploadPresignRequest.Builder builder = AbortMultipartUploadPresignRequest.builder(); + request.accept(builder); + return presignAbortMultipartUpload(builder.build()); + } + /** * A builder for creating {@link S3Presigner}s. Created using {@link #builder()}. */ diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/AbortMultipartUploadPresignRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/AbortMultipartUploadPresignRequest.java new file mode 100644 index 000000000000..5198c03ca4fc --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/AbortMultipartUploadPresignRequest.java @@ -0,0 +1,152 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Duration; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignRequest; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A request to pre-sign a {@link AbortMultipartUploadRequest} so that it can be executed at a later time without requiring + * additional signing or authentication. + * + * @see S3Presigner#presignAbortMultipartUpload(AbortMultipartUploadPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public final class AbortMultipartUploadPresignRequest + extends PresignRequest + implements ToCopyableBuilder { + private final AbortMultipartUploadRequest abortMultipartUploadRequest; + + private AbortMultipartUploadPresignRequest(DefaultBuilder builder) { + super(builder); + this.abortMultipartUploadRequest = Validate.notNull(builder.abortMultipartUploadRequest, "abortMultipartUploadRequest"); + } + + /** + * Create a builder that can be used to create a {@link AbortMultipartUploadPresignRequest}. + * + * @see S3Presigner#presignAbortMultipartUpload(AbortMultipartUploadPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + /** + * Retrieve the {@link AbortMultipartUploadRequest} that should be presigned. + */ + public AbortMultipartUploadRequest abortMultipartUploadRequest() { + return abortMultipartUploadRequest; + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + AbortMultipartUploadPresignRequest that = (AbortMultipartUploadPresignRequest) o; + + return abortMultipartUploadRequest.equals(that.abortMultipartUploadRequest); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + abortMultipartUploadRequest.hashCode(); + return result; + } + + /** + * A builder for a {@link AbortMultipartUploadPresignRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder + extends PresignRequest.Builder, + CopyableBuilder { + /** + * Configure the {@link AbortMultipartUploadRequest} that should be presigned. + */ + Builder abortMultipartUploadRequest(AbortMultipartUploadRequest abortMultipartUploadRequest); + + /** + * Configure the {@link AbortMultipartUploadRequest} that should be presigned. + *

    + * This is a convenience method for invoking {@link #abortMultipartUploadRequest(AbortMultipartUploadRequest)} + * without needing to invoke {@code AbortMultipartUploadRequest.builder()} or {@code build()}. + */ + default Builder abortMultipartUploadRequest(Consumer abortMultipartUploadRequest) { + AbortMultipartUploadRequest.Builder builder = AbortMultipartUploadRequest.builder(); + abortMultipartUploadRequest.accept(builder); + return abortMultipartUploadRequest(builder.build()); + } + + @Override + Builder signatureDuration(Duration signatureDuration); + + @Override + AbortMultipartUploadPresignRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder extends PresignRequest.DefaultBuilder implements Builder { + private AbortMultipartUploadRequest abortMultipartUploadRequest; + + private DefaultBuilder() { + } + + private DefaultBuilder(AbortMultipartUploadPresignRequest request) { + super(request); + this.abortMultipartUploadRequest = request.abortMultipartUploadRequest; + } + + @Override + public Builder abortMultipartUploadRequest(AbortMultipartUploadRequest abortMultipartUploadRequest) { + this.abortMultipartUploadRequest = abortMultipartUploadRequest; + return this; + } + + @Override + public AbortMultipartUploadPresignRequest build() { + return new AbortMultipartUploadPresignRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/CompleteMultipartUploadPresignRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/CompleteMultipartUploadPresignRequest.java new file mode 100644 index 000000000000..012d72222d03 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/CompleteMultipartUploadPresignRequest.java @@ -0,0 +1,154 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Duration; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A request to pre-sign a {@link CompleteMultipartUploadRequest} so that it can be executed at a later time without requiring + * additional signing or authentication. + * + * @see S3Presigner#presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public final class CompleteMultipartUploadPresignRequest + extends PresignRequest + implements ToCopyableBuilder { + private final CompleteMultipartUploadRequest completeMultipartUploadRequest; + + private CompleteMultipartUploadPresignRequest(DefaultBuilder builder) { + super(builder); + this.completeMultipartUploadRequest = Validate.notNull(builder.completeMultipartUploadRequest, + "completeMultipartUploadRequest"); + } + + /** + * Create a builder that can be used to create a {@link CompleteMultipartUploadPresignRequest}. + * + * @see S3Presigner#presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + /** + * Retrieve the {@link CompleteMultipartUploadRequest} that should be presigned. + */ + public CompleteMultipartUploadRequest completeMultipartUploadRequest() { + return completeMultipartUploadRequest; + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + CompleteMultipartUploadPresignRequest that = (CompleteMultipartUploadPresignRequest) o; + + return completeMultipartUploadRequest.equals(that.completeMultipartUploadRequest); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + completeMultipartUploadRequest.hashCode(); + return result; + } + + /** + * A builder for a {@link CompleteMultipartUploadPresignRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder + extends PresignRequest.Builder, + CopyableBuilder { + /** + * Configure the {@link CompleteMultipartUploadRequest} that should be presigned. + */ + Builder completeMultipartUploadRequest(CompleteMultipartUploadRequest completeMultipartUploadRequest); + + /** + * Configure the {@link CompleteMultipartUploadRequest} that should be presigned. + *

    + * This is a convenience method for invoking {@link #completeMultipartUploadRequest(CompleteMultipartUploadRequest)} + * without needing to invoke {@code CompleteMultipartUploadRequest.builder()} or {@code build()}. + */ + default Builder completeMultipartUploadRequest( + Consumer completeMultipartUploadRequest) { + CompleteMultipartUploadRequest.Builder builder = CompleteMultipartUploadRequest.builder(); + completeMultipartUploadRequest.accept(builder); + return completeMultipartUploadRequest(builder.build()); + } + + @Override + Builder signatureDuration(Duration signatureDuration); + + @Override + CompleteMultipartUploadPresignRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder extends PresignRequest.DefaultBuilder implements Builder { + private CompleteMultipartUploadRequest completeMultipartUploadRequest; + + private DefaultBuilder() { + } + + private DefaultBuilder(CompleteMultipartUploadPresignRequest request) { + super(request); + this.completeMultipartUploadRequest = request.completeMultipartUploadRequest; + } + + @Override + public Builder completeMultipartUploadRequest(CompleteMultipartUploadRequest completeMultipartUploadRequest) { + this.completeMultipartUploadRequest = completeMultipartUploadRequest; + return this; + } + + @Override + public CompleteMultipartUploadPresignRequest build() { + return new CompleteMultipartUploadPresignRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/CreateMultipartUploadPresignRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/CreateMultipartUploadPresignRequest.java new file mode 100644 index 000000000000..06531b295cc1 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/CreateMultipartUploadPresignRequest.java @@ -0,0 +1,154 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Duration; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A request to pre-sign a {@link CreateMultipartUploadRequest} so that it can be executed at a later time without requiring + * additional signing or authentication. + * + * @see S3Presigner#presignCreateMultipartUpload(CreateMultipartUploadPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public final class CreateMultipartUploadPresignRequest + extends PresignRequest + implements ToCopyableBuilder { + private final CreateMultipartUploadRequest createMultipartUploadRequest; + + private CreateMultipartUploadPresignRequest(DefaultBuilder builder) { + super(builder); + this.createMultipartUploadRequest = Validate.notNull(builder.createMultipartUploadRequest, + "createMultipartUploadRequest"); + } + + /** + * Create a builder that can be used to create a {@link CreateMultipartUploadPresignRequest}. + * + * @see S3Presigner#presignCreateMultipartUpload(CreateMultipartUploadPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + /** + * Retrieve the {@link CreateMultipartUploadRequest} that should be presigned. + */ + public CreateMultipartUploadRequest createMultipartUploadRequest() { + return createMultipartUploadRequest; + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + CreateMultipartUploadPresignRequest that = (CreateMultipartUploadPresignRequest) o; + + return createMultipartUploadRequest.equals(that.createMultipartUploadRequest); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + createMultipartUploadRequest.hashCode(); + return result; + } + + /** + * A builder for a {@link CreateMultipartUploadPresignRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder + extends PresignRequest.Builder, + CopyableBuilder { + /** + * Configure the {@link CreateMultipartUploadRequest} that should be presigned. + */ + Builder createMultipartUploadRequest(CreateMultipartUploadRequest createMultipartUploadRequest); + + /** + * Configure the {@link CreateMultipartUploadRequest} that should be presigned. + *

    + * This is a convenience method for invoking {@link #createMultipartUploadRequest(CreateMultipartUploadRequest)} + * without needing to invoke {@code CreateMultipartUploadRequest.builder()} or {@code build()}. + */ + default Builder createMultipartUploadRequest( + Consumer createMultipartUploadRequest) { + CreateMultipartUploadRequest.Builder builder = CreateMultipartUploadRequest.builder(); + createMultipartUploadRequest.accept(builder); + return createMultipartUploadRequest(builder.build()); + } + + @Override + Builder signatureDuration(Duration signatureDuration); + + @Override + CreateMultipartUploadPresignRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder extends PresignRequest.DefaultBuilder implements Builder { + private CreateMultipartUploadRequest createMultipartUploadRequest; + + private DefaultBuilder() { + } + + private DefaultBuilder(CreateMultipartUploadPresignRequest request) { + super(request); + this.createMultipartUploadRequest = request.createMultipartUploadRequest; + } + + @Override + public Builder createMultipartUploadRequest(CreateMultipartUploadRequest createMultipartUploadRequest) { + this.createMultipartUploadRequest = createMultipartUploadRequest; + return this; + } + + @Override + public CreateMultipartUploadPresignRequest build() { + return new CreateMultipartUploadPresignRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequest.java index 5b568a01506a..ce0c5d5fa2aa 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequest.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -129,7 +129,8 @@ default Builder getObjectRequest(Consumer getObjectReq private static final class DefaultBuilder extends PresignRequest.DefaultBuilder implements Builder { private GetObjectRequest getObjectRequest; - private DefaultBuilder() {} + private DefaultBuilder() { + } private DefaultBuilder(GetObjectPresignRequest request) { super(request); diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedAbortMultipartUploadRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedAbortMultipartUploadRequest.java new file mode 100644 index 000000000000..9848e647b6b4 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedAbortMultipartUploadRequest.java @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignedRequest; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.services.s3.model.AbortMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A pre-signed {@link AbortMultipartUploadRequest} that can be executed at a later time without requiring additional signing + * or authentication. + * + * @see S3Presigner#presignAbortMultipartUpload(AbortMultipartUploadPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public class PresignedAbortMultipartUploadRequest + extends PresignedRequest + implements ToCopyableBuilder { + private PresignedAbortMultipartUploadRequest(DefaultBuilder builder) { + super(builder); + } + + /** + * Create a builder that can be used to create a {@link PresignedAbortMultipartUploadRequest}. + * + * @see S3Presigner#presignAbortMultipartUpload(AbortMultipartUploadPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + /** + * A builder for a {@link PresignedAbortMultipartUploadRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder + extends PresignedRequest.Builder, + CopyableBuilder { + @Override + Builder expiration(Instant expiration); + + @Override + Builder isBrowserExecutable(Boolean isBrowserExecutable); + + @Override + Builder signedHeaders(Map> signedHeaders); + + @Override + Builder signedPayload(SdkBytes signedPayload); + + @Override + Builder httpRequest(SdkHttpRequest httpRequest); + + @Override + PresignedAbortMultipartUploadRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder + extends PresignedRequest.DefaultBuilder + implements Builder { + private DefaultBuilder() { + } + + private DefaultBuilder(PresignedAbortMultipartUploadRequest request) { + super(request); + } + + @Override + public PresignedAbortMultipartUploadRequest build() { + return new PresignedAbortMultipartUploadRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedCompleteMultipartUploadRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedCompleteMultipartUploadRequest.java new file mode 100644 index 000000000000..c06da5ecab06 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedCompleteMultipartUploadRequest.java @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignedRequest; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A pre-signed {@link CompleteMultipartUploadRequest} that can be executed at a later time without requiring additional signing + * or authentication. + * + * @see S3Presigner#presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public class PresignedCompleteMultipartUploadRequest + extends PresignedRequest + implements ToCopyableBuilder { + private PresignedCompleteMultipartUploadRequest(DefaultBuilder builder) { + super(builder); + } + + /** + * Create a builder that can be used to create a {@link PresignedCompleteMultipartUploadRequest}. + * + * @see S3Presigner#presignCompleteMultipartUpload(CompleteMultipartUploadPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + /** + * A builder for a {@link PresignedCompleteMultipartUploadRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder + extends PresignedRequest.Builder, + CopyableBuilder { + @Override + Builder expiration(Instant expiration); + + @Override + Builder isBrowserExecutable(Boolean isBrowserExecutable); + + @Override + Builder signedHeaders(Map> signedHeaders); + + @Override + Builder signedPayload(SdkBytes signedPayload); + + @Override + Builder httpRequest(SdkHttpRequest httpRequest); + + @Override + PresignedCompleteMultipartUploadRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder + extends PresignedRequest.DefaultBuilder + implements Builder { + private DefaultBuilder() { + } + + private DefaultBuilder(PresignedCompleteMultipartUploadRequest request) { + super(request); + } + + @Override + public PresignedCompleteMultipartUploadRequest build() { + return new PresignedCompleteMultipartUploadRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedCreateMultipartUploadRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedCreateMultipartUploadRequest.java new file mode 100644 index 000000000000..b16e16de1d1b --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedCreateMultipartUploadRequest.java @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignedRequest; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.services.s3.model.CreateMultipartUploadRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A pre-signed {@link CreateMultipartUploadRequest} that can be executed at a later time without requiring additional signing or + * authentication. + * + * @see S3Presigner#presignCreateMultipartUpload(CreateMultipartUploadPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public class PresignedCreateMultipartUploadRequest + extends PresignedRequest + implements ToCopyableBuilder { + private PresignedCreateMultipartUploadRequest(DefaultBuilder builder) { + super(builder); + } + + /** + * Create a builder that can be used to create a {@link PresignedCreateMultipartUploadRequest}. + * + * @see S3Presigner#presignCreateMultipartUpload(CreateMultipartUploadPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + /** + * A builder for a {@link PresignedCreateMultipartUploadRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder + extends PresignedRequest.Builder, + CopyableBuilder { + @Override + Builder expiration(Instant expiration); + + @Override + Builder isBrowserExecutable(Boolean isBrowserExecutable); + + @Override + Builder signedHeaders(Map> signedHeaders); + + @Override + Builder signedPayload(SdkBytes signedPayload); + + @Override + Builder httpRequest(SdkHttpRequest httpRequest); + + @Override + PresignedCreateMultipartUploadRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder + extends PresignedRequest.DefaultBuilder + implements Builder { + private DefaultBuilder() { + } + + private DefaultBuilder(PresignedCreateMultipartUploadRequest request) { + super(request); + } + + @Override + public PresignedCreateMultipartUploadRequest build() { + return new PresignedCreateMultipartUploadRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequest.java index 6f03eb623bca..f9cacffae249 100644 --- a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequest.java +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -92,7 +92,8 @@ public interface Builder extends PresignedRequest.Builder, private static final class DefaultBuilder extends PresignedRequest.DefaultBuilder implements Builder { - private DefaultBuilder() { } + private DefaultBuilder() { + } private DefaultBuilder(PresignedGetObjectRequest request) { super(request); diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedPutObjectRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedPutObjectRequest.java new file mode 100644 index 000000000000..003a7ab0343b --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedPutObjectRequest.java @@ -0,0 +1,107 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignedRequest; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A pre-signed a {@link PutObjectRequest} that can be executed at a later time without requiring additional signing or + * authentication. + * + * @see S3Presigner#presignPutObject(PutObjectPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public class PresignedPutObjectRequest + extends PresignedRequest + implements ToCopyableBuilder { + private PresignedPutObjectRequest(DefaultBuilder builder) { + super(builder); + } + + /** + * Create a builder that can be used to create a {@link PresignedPutObjectRequest}. + * + * @see S3Presigner#presignPutObject(PutObjectPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + /** + * A builder for a {@link PresignedPutObjectRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder extends PresignedRequest.Builder, + CopyableBuilder { + @Override + Builder expiration(Instant expiration); + + @Override + Builder isBrowserExecutable(Boolean isBrowserExecutable); + + @Override + Builder signedHeaders(Map> signedHeaders); + + @Override + Builder signedPayload(SdkBytes signedPayload); + + @Override + Builder httpRequest(SdkHttpRequest httpRequest); + + @Override + PresignedPutObjectRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder + extends PresignedRequest.DefaultBuilder + implements Builder { + private DefaultBuilder() { + } + + private DefaultBuilder(PresignedPutObjectRequest request) { + super(request); + } + + @Override + public PresignedPutObjectRequest build() { + return new PresignedPutObjectRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedUploadPartRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedUploadPartRequest.java new file mode 100644 index 000000000000..89dcc6b9d68a --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PresignedUploadPartRequest.java @@ -0,0 +1,107 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Instant; +import java.util.List; +import java.util.Map; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignedRequest; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A pre-signed {@link UploadPartRequest} that can be executed at a later time without requiring additional signing or + * authentication. + * + * @see S3Presigner#presignUploadPart(UploadPartPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public class PresignedUploadPartRequest + extends PresignedRequest + implements ToCopyableBuilder { + private PresignedUploadPartRequest(DefaultBuilder builder) { + super(builder); + } + + /** + * Create a builder that can be used to create a {@link PresignedUploadPartRequest}. + * + * @see S3Presigner#presignUploadPart(UploadPartPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + /** + * A builder for a {@link PresignedUploadPartRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder extends PresignedRequest.Builder, + CopyableBuilder { + @Override + Builder expiration(Instant expiration); + + @Override + Builder isBrowserExecutable(Boolean isBrowserExecutable); + + @Override + Builder signedHeaders(Map> signedHeaders); + + @Override + Builder signedPayload(SdkBytes signedPayload); + + @Override + Builder httpRequest(SdkHttpRequest httpRequest); + + @Override + PresignedUploadPartRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder + extends PresignedRequest.DefaultBuilder + implements Builder { + private DefaultBuilder() { + } + + private DefaultBuilder(PresignedUploadPartRequest request) { + super(request); + } + + @Override + public PresignedUploadPartRequest build() { + return new PresignedUploadPartRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PutObjectPresignRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PutObjectPresignRequest.java new file mode 100644 index 000000000000..2d02b0b079f8 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/PutObjectPresignRequest.java @@ -0,0 +1,151 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Duration; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignRequest; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A request to pre-sign a {@link PutObjectRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + * + * @see S3Presigner#presignPutObject(PutObjectPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public final class PutObjectPresignRequest + extends PresignRequest + implements ToCopyableBuilder { + private final PutObjectRequest putObjectRequest; + + private PutObjectPresignRequest(DefaultBuilder builder) { + super(builder); + this.putObjectRequest = Validate.notNull(builder.putObjectRequest, "putObjectRequest"); + } + + /** + * Create a builder that can be used to create a {@link PutObjectPresignRequest}. + * + * @see S3Presigner#presignPutObject(PutObjectPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + /** + * Retrieve the {@link PutObjectRequest} that should be presigned. + */ + public PutObjectRequest putObjectRequest() { + return putObjectRequest; + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + PutObjectPresignRequest that = (PutObjectPresignRequest) o; + + return putObjectRequest.equals(that.putObjectRequest); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + putObjectRequest.hashCode(); + return result; + } + + /** + * A builder for a {@link PutObjectPresignRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder extends PresignRequest.Builder, + CopyableBuilder { + /** + * Configure the {@link PutObjectRequest} that should be presigned. + */ + Builder putObjectRequest(PutObjectRequest putObjectRequest); + + /** + * Configure the {@link PutObjectRequest} that should be presigned. + *

    + * This is a convenience method for invoking {@link #putObjectRequest(PutObjectRequest)} without needing to invoke + * {@code PutObjectRequest.builder()} or {@code build()}. + */ + default Builder putObjectRequest(Consumer putObjectRequest) { + PutObjectRequest.Builder builder = PutObjectRequest.builder(); + putObjectRequest.accept(builder); + return putObjectRequest(builder.build()); + } + + @Override + Builder signatureDuration(Duration signatureDuration); + + @Override + PutObjectPresignRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder extends PresignRequest.DefaultBuilder implements Builder { + private PutObjectRequest putObjectRequest; + + private DefaultBuilder() { + } + + private DefaultBuilder(PutObjectPresignRequest request) { + super(request); + this.putObjectRequest = request.putObjectRequest; + } + + @Override + public Builder putObjectRequest(PutObjectRequest putObjectRequest) { + this.putObjectRequest = putObjectRequest; + return this; + } + + @Override + public PutObjectPresignRequest build() { + return new PutObjectPresignRequest(this); + } + } +} diff --git a/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/UploadPartPresignRequest.java b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/UploadPartPresignRequest.java new file mode 100644 index 000000000000..9b8dc595f191 --- /dev/null +++ b/services/s3/src/main/java/software/amazon/awssdk/services/s3/presigner/model/UploadPartPresignRequest.java @@ -0,0 +1,151 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; + +import java.time.Duration; +import java.util.function.Consumer; +import software.amazon.awssdk.annotations.Immutable; +import software.amazon.awssdk.annotations.NotThreadSafe; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.annotations.SdkPublicApi; +import software.amazon.awssdk.annotations.ThreadSafe; +import software.amazon.awssdk.awscore.presigner.PresignRequest; +import software.amazon.awssdk.services.s3.model.UploadPartRequest; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.utils.Validate; +import software.amazon.awssdk.utils.builder.CopyableBuilder; +import software.amazon.awssdk.utils.builder.ToCopyableBuilder; + +/** + * A request to pre-sign a {@link UploadPartRequest} so that it can be executed at a later time without requiring additional + * signing or authentication. + * + * @see S3Presigner#presignUploadPart(UploadPartPresignRequest) + * @see #builder() + */ +@SdkPublicApi +@Immutable +@ThreadSafe +public final class UploadPartPresignRequest + extends PresignRequest + implements ToCopyableBuilder { + private final UploadPartRequest uploadPartRequest; + + private UploadPartPresignRequest(DefaultBuilder builder) { + super(builder); + this.uploadPartRequest = Validate.notNull(builder.uploadPartRequest, "uploadPartRequest"); + } + + /** + * Create a builder that can be used to create a {@link UploadPartPresignRequest}. + * + * @see S3Presigner#presignUploadPart(UploadPartPresignRequest) + */ + public static Builder builder() { + return new DefaultBuilder(); + } + + /** + * Retrieve the {@link UploadPartRequest} that should be presigned. + */ + public UploadPartRequest uploadPartRequest() { + return uploadPartRequest; + } + + @Override + public Builder toBuilder() { + return new DefaultBuilder(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + + UploadPartPresignRequest that = (UploadPartPresignRequest) o; + + return uploadPartRequest.equals(that.uploadPartRequest); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + uploadPartRequest.hashCode(); + return result; + } + + /** + * A builder for a {@link UploadPartPresignRequest}, created with {@link #builder()}. + */ + @SdkPublicApi + @NotThreadSafe + public interface Builder extends PresignRequest.Builder, + CopyableBuilder { + /** + * Configure the {@link UploadPartRequest} that should be presigned. + */ + Builder uploadPartRequest(UploadPartRequest uploadPartRequest); + + /** + * Configure the {@link UploadPartRequest} that should be presigned. + *

    + * This is a convenience method for invoking {@link #uploadPartRequest(UploadPartRequest)} without needing to invoke + * {@code UploadPartRequest.builder()} or {@code build()}. + */ + default Builder uploadPartRequest(Consumer uploadPartRequest) { + UploadPartRequest.Builder builder = UploadPartRequest.builder(); + uploadPartRequest.accept(builder); + return uploadPartRequest(builder.build()); + } + + @Override + Builder signatureDuration(Duration signatureDuration); + + @Override + UploadPartPresignRequest build(); + } + + @SdkInternalApi + private static final class DefaultBuilder extends PresignRequest.DefaultBuilder implements Builder { + private UploadPartRequest uploadPartRequest; + + private DefaultBuilder() { + } + + private DefaultBuilder(UploadPartPresignRequest request) { + super(request); + this.uploadPartRequest = request.uploadPartRequest; + } + + @Override + public Builder uploadPartRequest(UploadPartRequest uploadPartRequest) { + this.uploadPartRequest = uploadPartRequest; + return this; + } + + @Override + public UploadPartPresignRequest build() { + return new UploadPartPresignRequest(this); + } + } +} diff --git a/services/s3/src/main/resources/codegen-resources/customization.config b/services/s3/src/main/resources/codegen-resources/customization.config index e861f54701a6..e284b3aa21cf 100644 --- a/services/s3/src/main/resources/codegen-resources/customization.config +++ b/services/s3/src/main/resources/codegen-resources/customization.config @@ -16,6 +16,20 @@ } ] }, + "CopyObjectRequest": { + "modify": [ + { + "Bucket": { + "emitPropertyName": "DestinationBucket", + "existingNameDeprecated": true + }, + "Key": { + "emitPropertyName": "DestinationKey", + "existingNameDeprecated": true + } + } + ] + }, "ObjectVersion": { "modify": [ { @@ -61,6 +75,12 @@ "memberName": "Tagging", "convenienceType": "software.amazon.awssdk.services.s3.model.Tagging", "typeAdapterFqcn": "software.amazon.awssdk.services.s3.internal.TaggingAdapter" + }, + { + "shapeName": "CreateMultipartUploadRequest", + "memberName": "Tagging", + "convenienceType": "software.amazon.awssdk.services.s3.model.Tagging", + "typeAdapterFqcn": "software.amazon.awssdk.services.s3.internal.TaggingAdapter" } ], "customResponseMetadata": { @@ -85,6 +105,5 @@ "createMethodParams": [ "clientConfiguration" ] - }, - "serviceSpecificHttpConfig": "software.amazon.awssdk.services.s3.internal.S3HttpConfigurationOptions" + } } diff --git a/services/s3/src/main/resources/codegen-resources/service-2.json b/services/s3/src/main/resources/codegen-resources/service-2.json index 218cfc465b76..027a0f0d6ea2 100644 --- a/services/s3/src/main/resources/codegen-resources/service-2.json +++ b/services/s3/src/main/resources/codegen-resources/service-2.json @@ -26,7 +26,7 @@ {"shape":"NoSuchUpload"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadAbort.html", - "documentation":"

    Aborts a multipart upload.

    To verify that all parts have been removed, so you don't get charged for the part storage, you should call the List Parts operation and ensure the parts list is empty.

    " + "documentation":"

    This operation aborts a multipart upload. After a multipart upload is aborted, no additional parts can be uploaded using that upload ID. The storage consumed by any previously uploaded parts will be freed. However, if any part uploads are currently in progress, those part uploads might or might not succeed. As a result, it might be necessary to abort a given multipart upload multiple times in order to completely free all storage consumed by all parts.

    To verify that all parts have been removed, so you don't get charged for the part storage, you should call the ListParts operation and ensure that the parts list is empty.

    For information about permissions required to use the multipart upload API, see Multipart Upload API and Permissions.

    The following operations are related to AbortMultipartUpload:

    " }, "CompleteMultipartUpload":{ "name":"CompleteMultipartUpload", @@ -37,7 +37,7 @@ "input":{"shape":"CompleteMultipartUploadRequest"}, "output":{"shape":"CompleteMultipartUploadOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadComplete.html", - "documentation":"

    Completes a multipart upload by assembling previously uploaded parts.

    " + "documentation":"

    Completes a multipart upload by assembling previously uploaded parts.

    You first initiate the multipart upload and then upload all parts using the UploadPart operation. After successfully uploading all relevant parts of an upload, you call this operation to complete the upload. Upon receiving this request, Amazon S3 concatenates all the parts in ascending order by part number to create a new object. In the Complete Multipart Upload request, you must provide the parts list. You must ensure that the parts list is complete. This operation concatenates the parts that you provide in the list. For each part in the list, you must provide the part number and the ETag value, returned after that part was uploaded.

    Processing of a Complete Multipart Upload request could take several minutes to complete. After Amazon S3 begins processing the request, it sends an HTTP response header that specifies a 200 OK response. While processing is in progress, Amazon S3 periodically sends white space characters to keep the connection from timing out. Because a request could fail after the initial 200 OK response has been sent, it is important that you check the response body to determine whether the request succeeded.

    Note that if CompleteMultipartUpload fails, applications should be prepared to retry the failed requests. For more information, see Amazon S3 Error Best Practices.

    For more information about multipart uploads, see Uploading Objects Using Multipart Upload.

    For information about permissions required to use the multipart upload API, see Multipart Upload API and Permissions.

    GetBucketLifecycle has the following special errors:

    • Error code: EntityTooSmall

      • Description: Your proposed upload is smaller than the minimum allowed object size. Each part must be at least 5 MB in size, except the last part.

      • 400 Bad Request

    • Error code: InvalidPart

      • Description: One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag.

      • 400 Bad Request

    • Error code: InvalidPartOrder

      • Description: The list of parts was not in ascending order. The parts list must be specified in order by part number.

      • 400 Bad Request

    • Error code: NoSuchUpload

      • Description: The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.

      • 404 Not Found

    The following operations are related to DeleteBucketMetricsConfiguration:

    " }, "CopyObject":{ "name":"CopyObject", @@ -51,7 +51,7 @@ {"shape":"ObjectNotInActiveTierError"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectCOPY.html", - "documentation":"

    Creates a copy of an object that is already stored in Amazon S3.

    ", + "documentation":"

    Creates a copy of an object that is already stored in Amazon S3.

    You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your object up to 5 GB in size in a single atomic operation using this API. However, for copying an object greater than 5 GB, you must use the multipart upload Upload Part - Copy API. For more information, see Copy Object Using the REST Multipart Upload API.

    When copying an object, you can preserve all metadata (default) or specify new metadata. However, the ACL is not preserved and is set to private for the user making the request. To override the default ACL setting, specify a new ACL when generating a copy request. For more information, see Using ACLs.

    Amazon S3 transfer acceleration does not support cross-region copies. If you request a cross-region copy using a transfer acceleration endpoint, you get a 400 Bad Request error. For more information about transfer acceleration, see Transfer Acceleration.

    All copy requests must be authenticated. Additionally, you must have read access to the source object and write access to the destination bucket. For more information, see REST Authentication. Both the Region that you want to copy the object from and the Region that you want to copy the object to must be enabled for your account.

    To only copy an object under certain conditions, such as whether the Etag matches or whether the object was modified before or after a specified date, use the request parameters x-amz-copy-source-if-match, x-amz-copy-source-if-none-match, x-amz-copy-source-if-unmodified-since, or x-amz-copy-source-if-modified-since.

    All headers with the x-amz- prefix, including x-amz-copy-source, must be signed.

    You can use this operation to change the storage class of an object that is already stored in Amazon S3 using the StorageClass parameter. For more information, see Storage Classes.

    The source object that you are copying can be encrypted or unencrypted. If the source object is encrypted, it can be encrypted by server-side encryption using AWS managed encryption keys or by using a customer-provided encryption key. When copying an object, you can request that Amazon S3 encrypt the target object by using either the AWS managed encryption keys or by using your own encryption key. You can do this regardless of the form of server-side encryption that was used to encrypt the source, or even if the source object was not encrypted. For more information about server-side encryption, see Using Server-Side Encryption.

    A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3 is copying the files. If the error occurs before the copy operation starts, you receive a standard Amazon S3 error. If the error occurs during the copy operation, the error response is embedded in the 200 OK response. This means that a 200 OK response can contain either a success or an error. Design your application to parse the contents of the response and handle it appropriately.

    If the copy is successful, you receive a response with information about the copied object.

    If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not, it would not contain the content-length, and you would need to read the entire body.

    Consider the following when using request headers:

    • Consideration 1 – If both the x-amz-copy-source-if-match and x-amz-copy-source-if-unmodified-since headers are present in the request and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

      • x-amz-copy-source-if-match condition evaluates to true

      • x-amz-copy-source-if-unmodified-since condition evaluates to false

    • Consideration 2 – If both of the x-amz-copy-source-if-none-match and x-amz-copy-source-if-modified-since headers are present in the request and evaluate as follows, Amazon S3 returns the 412 Precondition Failed response code:

      • x-amz-copy-source-if-none-match condition evaluates to false

      • x-amz-copy-source-if-modified-since condition evaluates to true

    The copy request charge is based on the storage class and Region you specify for the destination object. For pricing information, see Amazon S3 Pricing.

    Following are other considerations when using CopyObject:

    Versioning

    By default, x-amz-copy-source identifies the current version of an object to copy. (If the current version is a delete marker, Amazon S3 behaves as if the object was deleted.) To copy a different version, use the versionId subresource.

    If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for the object being copied. This version ID is different from the version ID of the source object. Amazon S3 returns the version ID of the copied object in the x-amz-version-id response header in the response.

    If you do not enable versioning or suspend it on the target bucket, the version ID that Amazon S3 generates is always null.

    If the source object's storage class is GLACIER, you must restore a copy of this object before you can use it as a source object for the copy operation. For more information, see .

    Access Permissions

    When copying an object, you can optionally specify the accounts or groups that should be granted specific permissions on the new object. There are two ways to grant the permissions using the request headers:

    • Specify a canned ACL with the x-amz-acl request header. For more information, see Canned ACL.

    • Specify access permissions explicitly with the x-amz-grant-read, x-amz-grant-read-acp, x-amz-grant-write-acp, and x-amz-grant-full-control headers. These parameters map to the set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview.

    You can use either a canned ACL or specify access permissions explicitly. You cannot do both.

    Server-Side- Encryption-Specific Request Headers

    To encrypt the target object, you must provide the appropriate encryption-related request headers. The one you use depends on whether you want to use AWS managed encryption keys or provide your own encryption key.

    • To encrypt the target object using server-side encryption with an AWS managed encryption key, provide the following request headers, as appropriate.

      • x-amz-server-side​-encryption

      • x-amz-server-side-encryption-aws-kms-key-id

      • x-amz-server-side-encryption-context

      If you specify x-amz-server-side-encryption:aws:kms, but don't provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the AWS managed CMK in AWS KMS to protect the data. If you want to use a customer managed AWS KMS CMK, you must provide the x-amz-server-side-encryption-aws-kms-key-id of the symmetric customer managed CMK. Amazon S3 only supports symmetric CMKs and not asymmetric CMKs. For more information, see Using Symmetric and Asymmetric Keys in the AWS Key Management Service Developer Guide.

      All GET and PUT requests for an object protected by AWS KMS fail if you don't make them with SSL or by using SigV4.

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in KMS.

    • To encrypt the target object using server-side encryption with an encryption key that you provide, use the following headers.

      • x-amz-server-side​-encryption​-customer-algorithm

      • x-amz-server-side​-encryption​-customer-key

      • x-amz-server-side​-encryption​-customer-key-MD5

    • If the source object is encrypted using server-side encryption with customer-provided encryption keys, you must use the following headers.

      • x-amz-copy-source​-server-side​-encryption​-customer-algorithm

      • x-amz-copy-source​-server-side​-encryption​-customer-key

      • x-amz-copy-source-​server-side​-encryption​-customer-key-MD5

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in Amazon KMS.

    Access-Control-List (ACL)-Specific Request Headers

    You also can use the following access control–related headers with this operation. By default, all objects are private. Only the owner has full access control. When adding a new object, you can grant permissions to individual AWS accounts or to predefined groups defined by Amazon S3. These permissions are then added to the access control list (ACL) on the object. For more information, see Using ACLs. With this operation, you can grant access permissions using one of the following two methods:

    • Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set of grantees and permissions. For more information, see Canned ACL.

    • Specify access permissions explicitly — To explicitly grant access permissions to specific AWS accounts or groups, use the following headers. Each header maps to specific permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview. In the header, you specify a list of grantees who get the specific permission. To grant permissions explicitly, use:

      • x-amz-grant-read

      • x-amz-grant-write

      • x-amz-grant-read-acp

      • x-amz-grant-write-acp

      • x-amz-grant-full-control

      You specify each grantee as a type=value pair, where the type is one of the following:

      • emailAddress – if the value specified is the email address of an AWS account

      • id – if the value specified is the canonical user ID of an AWS account

      • uri – if you are granting permissions to a predefined group

      For example, the following x-amz-grant-read header grants the AWS accounts identified by email addresses permissions to read object data and its metadata:

      x-amz-grant-read: emailAddress=\"xyz@amazon.com\", emailAddress=\"abc@amazon.com\"

    The following operations are related to CopyObject:

    For more information, see Copying Objects.

    ", "alias":"PutObjectCopy" }, "CreateBucket":{ @@ -67,7 +67,7 @@ {"shape":"BucketAlreadyOwnedByYou"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUT.html", - "documentation":"

    Creates a new bucket.

    ", + "documentation":"

    Creates a new bucket. To create a bucket, you must register with Amazon S3 and have a valid AWS Access Key ID to authenticate requests. Anonymous requests are never allowed to create buckets. By creating the bucket, you become the bucket owner.

    Not every string is an acceptable bucket name. For information on bucket naming restrictions, see Working with Amazon S3 Buckets.

    By default, the bucket is created in the US East (N. Virginia) Region. You can optionally specify a Region in the request body. You might choose a Region to optimize latency, minimize costs, or address regulatory requirements. For example, if you reside in Europe, you will probably find it advantageous to create buckets in the EU (Ireland) Region. For more information, see How to Select a Region for Your Buckets.

    If you send your create bucket request to the s3.amazonaws.com endpoint, the request goes to the us-east-1 Region. Accordingly, the signature calculations in Signature Version 4 must use us-east-1 as the Region, even if the location constraint in the request specifies another Region where the bucket is to be created. If you create a bucket in a Region other than US East (N. Virginia), your application must be able to handle 307 redirect. For more information, see Virtual Hosting of Buckets.

    When creating a bucket using this operation, you can optionally specify the accounts or groups that should be granted specific permissions on the bucket. There are two ways to grant the appropriate permissions using the request headers.

    • Specify a canned ACL using the x-amz-acl request header. Amazon S3 supports a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set of grantees and permissions. For more information, see Canned ACL.

    • Specify access permissions explicitly using the x-amz-grant-read, x-amz-grant-write, x-amz-grant-read-acp, x-amz-grant-write-acp, and x-amz-grant-full-control headers. These headers map to the set of permissions Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview.

      You specify each grantee as a type=value pair, where the type is one of the following:

      • emailAddress – if the value specified is the email address of an AWS account

      • id – if the value specified is the canonical user ID of an AWS account

      • uri – if you are granting permissions to a predefined group

      For example, the following x-amz-grant-read header grants the AWS accounts identified by email addresses permissions to read object data and its metadata:

      x-amz-grant-read: emailAddress=\"xyz@amazon.com\", emailAddress=\"abc@amazon.com\"

    You can use either a canned ACL or specify access permissions explicitly. You cannot do both.

    The following operations are related to CreateBucket:

    ", "alias":"PutBucket" }, "CreateMultipartUpload":{ @@ -79,7 +79,7 @@ "input":{"shape":"CreateMultipartUploadRequest"}, "output":{"shape":"CreateMultipartUploadOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadInitiate.html", - "documentation":"

    Initiates a multipart upload and returns an upload ID.

    Note: After you initiate multipart upload and upload one or more parts, you must either complete or abort multipart upload in order to stop getting charged for storage of the uploaded parts. Only after you either complete or abort multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts storage.

    ", + "documentation":"

    This operation initiates a multipart upload and returns an upload ID. This upload ID is used to associate all of the parts in the specific multipart upload. You specify this upload ID in each of your subsequent upload part requests (see UploadPart). You also include this upload ID in the final request to either complete or abort the multipart upload request.

    For more information about multipart uploads, see Multipart Upload Overview.

    If you have configured a lifecycle rule to abort incomplete multipart uploads, the upload must complete within the number of days specified in the bucket lifecycle configuration. Otherwise, the incomplete multipart upload becomes eligible for an abort operation and Amazon S3 aborts the multipart upload. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

    For information about the permissions required to use the multipart upload API, see Multipart Upload API and Permissions.

    For request signing, multipart upload is just a series of regular requests. You initiate a multipart upload, send one or more requests to upload parts, and then complete the multipart upload process. You sign each request individually. There is nothing special about signing multipart upload requests. For more information about signing, see Authenticating Requests (AWS Signature Version 4).

    After you initiate a multipart upload and upload one or more parts, to stop being charged for storing the uploaded parts, you must either complete or abort the multipart upload. Amazon S3 frees up the space used to store the parts and stop charging you for storing them only after you either complete or abort a multipart upload.

    You can optionally request server-side encryption. For server-side encryption, Amazon S3 encrypts your data as it writes it to disks in its data centers and decrypts it when you access it. You can provide your own encryption key, or use AWS Key Management Service (AWS KMS) customer master keys (CMKs) or Amazon S3-managed encryption keys. If you choose to provide your own encryption key, the request headers you provide in UploadPart) and UploadPartCopy) requests must match the headers you used in the request to initiate the upload by using CreateMultipartUpload.

    To perform a multipart upload with encryption using an AWS KMS CMK, the requester must have permission to the kms:Encrypt, kms:Decrypt, kms:ReEncrypt*, kms:GenerateDataKey*, and kms:DescribeKey actions on the key. These permissions are required because Amazon S3 must decrypt and read data from the encrypted file parts before it completes the multipart upload.

    If your AWS Identity and Access Management (IAM) user or role is in the same AWS account as the AWS KMS CMK, then you must have these permissions on the key policy. If your IAM user or role belongs to a different account than the key, then you must have the permissions on both the key policy and your IAM user or role.

    For more information, see Protecting Data Using Server-Side Encryption.

    Access Permissions

    When copying an object, you can optionally specify the accounts or groups that should be granted specific permissions on the new object. There are two ways to grant the permissions using the request headers:

    • Specify a canned ACL with the x-amz-acl request header. For more information, see Canned ACL.

    • Specify access permissions explicitly with the x-amz-grant-read, x-amz-grant-read-acp, x-amz-grant-write-acp, and x-amz-grant-full-control headers. These parameters map to the set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview.

    You can use either a canned ACL or specify access permissions explicitly. You cannot do both.

    Server-Side- Encryption-Specific Request Headers

    You can optionally tell Amazon S3 to encrypt data at rest using server-side encryption. Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it writes it to disks in its data centers and decrypts it when you access it. The option you use depends on whether you want to use AWS managed encryption keys or provide your own encryption key.

    • Use encryption keys managed by Amazon S3 or customer master keys (CMKs) stored in AWS Key Management Service (AWS KMS) – If you want AWS to manage the keys used to encrypt data, specify the following headers in the request.

      • x-amz-server-side​-encryption

      • x-amz-server-side-encryption-aws-kms-key-id

      • x-amz-server-side-encryption-context

      If you specify x-amz-server-side-encryption:aws:kms, but don't provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the AWS managed CMK in AWS KMS to protect the data.

      All GET and PUT requests for an object protected by AWS KMS fail if you don't make them with SSL or by using SigV4.

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in AWS KMS.

    • Use customer-provided encryption keys – If you want to manage your own encryption keys, provide all the following headers in the request.

      • x-amz-server-side​-encryption​-customer-algorithm

      • x-amz-server-side​-encryption​-customer-key

      • x-amz-server-side​-encryption​-customer-key-MD5

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in AWS KMS.

    Access-Control-List (ACL)-Specific Request Headers

    You also can use the following access control–related headers with this operation. By default, all objects are private. Only the owner has full access control. When adding a new object, you can grant permissions to individual AWS accounts or to predefined groups defined by Amazon S3. These permissions are then added to the access control list (ACL) on the object. For more information, see Using ACLs. With this operation, you can grant access permissions using one of the following two methods:

    • Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set of grantees and permissions. For more information, see Canned ACL.

    • Specify access permissions explicitly — To explicitly grant access permissions to specific AWS accounts or groups, use the following headers. Each header maps to specific permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview. In the header, you specify a list of grantees who get the specific permission. To grant permissions explicitly, use:

      • x-amz-grant-read

      • x-amz-grant-write

      • x-amz-grant-read-acp

      • x-amz-grant-write-acp

      • x-amz-grant-full-control

      You specify each grantee as a type=value pair, where the type is one of the following:

      • emailAddress – if the value specified is the email address of an AWS account

      • id – if the value specified is the canonical user ID of an AWS account

      • uri – if you are granting permissions to a predefined group

      For example, the following x-amz-grant-read header grants the AWS accounts identified by email addresses permissions to read object data and its metadata:

      x-amz-grant-read: emailAddress=\"xyz@amazon.com\", emailAddress=\"abc@amazon.com\"

    The following operations are related to CreateMultipartUpload:

    ", "alias":"InitiateMultipartUpload" }, "DeleteBucket":{ @@ -91,7 +91,7 @@ }, "input":{"shape":"DeleteBucketRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETE.html", - "documentation":"

    Deletes the bucket. All objects (including all object versions and Delete Markers) in the bucket must be deleted before the bucket itself can be deleted.

    " + "documentation":"

    Deletes the bucket. All objects (including all object versions and delete markers) in the bucket must be deleted before the bucket itself can be deleted.

    Related Resources

    " }, "DeleteBucketAnalyticsConfiguration":{ "name":"DeleteBucketAnalyticsConfiguration", @@ -101,7 +101,7 @@ "responseCode":204 }, "input":{"shape":"DeleteBucketAnalyticsConfigurationRequest"}, - "documentation":"

    Deletes an analytics configuration for the bucket (specified by the analytics configuration ID).

    To use this operation, you must have permissions to perform the s3:PutAnalyticsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others.

    " + "documentation":"

    Deletes an analytics configuration for the bucket (specified by the analytics configuration ID).

    To use this operation, you must have permissions to perform the s3:PutAnalyticsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about the Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class Analysis.

    The following operations are related to DeleteBucketAnalyticsConfiguration:

    " }, "DeleteBucketCors":{ "name":"DeleteBucketCors", @@ -112,7 +112,7 @@ }, "input":{"shape":"DeleteBucketCorsRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEcors.html", - "documentation":"

    Deletes the CORS configuration information set for the bucket.

    " + "documentation":"

    Deletes the cors configuration information set for the bucket.

    To use this operation, you must have permission to perform the s3:PutBucketCORS action. The bucket owner has this permission by default and can grant this permission to others.

    For information about cors, see Enabling Cross-Origin Resource Sharing in the Amazon Simple Storage Service Developer Guide.

    Related Resources:

    " }, "DeleteBucketEncryption":{ "name":"DeleteBucketEncryption", @@ -122,7 +122,7 @@ "responseCode":204 }, "input":{"shape":"DeleteBucketEncryptionRequest"}, - "documentation":"

    Deletes the server-side encryption configuration from the bucket.

    " + "documentation":"

    This implementation of the DELETE operation removes default encryption from the bucket. For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption in the Amazon Simple Storage Service Developer Guide.

    To use this operation, you must have permissions to perform the s3:PutEncryptionConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    Related Resources

    " }, "DeleteBucketInventoryConfiguration":{ "name":"DeleteBucketInventoryConfiguration", @@ -132,7 +132,7 @@ "responseCode":204 }, "input":{"shape":"DeleteBucketInventoryConfigurationRequest"}, - "documentation":"

    Deletes an inventory configuration (identified by the inventory ID) from the bucket.

    " + "documentation":"

    Deletes an inventory configuration (identified by the inventory ID) from the bucket.

    To use this operation, you must have permissions to perform the s3:PutInventoryConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

    Operations related to DeleteBucketInventoryConfiguration include:

    " }, "DeleteBucketLifecycle":{ "name":"DeleteBucketLifecycle", @@ -143,7 +143,7 @@ }, "input":{"shape":"DeleteBucketLifecycleRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETElifecycle.html", - "documentation":"

    Deletes the lifecycle configuration from the bucket.

    " + "documentation":"

    Deletes the lifecycle configuration from the specified bucket. Amazon S3 removes all the lifecycle configuration rules in the lifecycle subresource associated with the bucket. Your objects never expire, and Amazon S3 no longer automatically deletes any objects on the basis of rules contained in the deleted lifecycle configuration.

    To use this operation, you must have permission to perform the s3:PutLifecycleConfiguration action. By default, the bucket owner has this permission and the bucket owner can grant this permission to others.

    There is usually some time lag before lifecycle configuration deletion is fully propagated to all the Amazon S3 systems.

    For more information about the object expiration, see Elements to Describe Lifecycle Actions.

    Related actions include:

    " }, "DeleteBucketMetricsConfiguration":{ "name":"DeleteBucketMetricsConfiguration", @@ -153,7 +153,7 @@ "responseCode":204 }, "input":{"shape":"DeleteBucketMetricsConfigurationRequest"}, - "documentation":"

    Deletes a metrics configuration (specified by the metrics configuration ID) from the bucket.

    " + "documentation":"

    Deletes a metrics configuration for the Amazon CloudWatch request metrics (specified by the metrics configuration ID) from the bucket. Note that this doesn't include the daily storage metrics.

    To use this operation, you must have permissions to perform the s3:PutMetricsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon CloudWatch.

    The following operations are related to DeleteBucketMetricsConfiguration:

    " }, "DeleteBucketPolicy":{ "name":"DeleteBucketPolicy", @@ -164,7 +164,7 @@ }, "input":{"shape":"DeleteBucketPolicyRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEpolicy.html", - "documentation":"

    Deletes the policy from the bucket.

    " + "documentation":"

    This implementation of the DELETE operation uses the policy subresource to delete the policy of a specified bucket. If you are using an identity other than the root user of the AWS account that owns the bucket, the calling identity must have the DeleteBucketPolicy permissions on the specified bucket and belong to the bucket owner's account to use this operation.

    If you don't have DeleteBucketPolicy permissions, Amazon S3 returns a 403 Access Denied error. If you have the correct permissions, but you're not using an identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not Allowed error.

    As a security precaution, the root user of the AWS account that owns a bucket can always use this operation, even if the policy explicitly denies the root user the ability to perform this action.

    For more information about bucket policies, see Using Bucket Policies and UserPolicies.

    The following operations are related to DeleteBucketPolicy

    " }, "DeleteBucketReplication":{ "name":"DeleteBucketReplication", @@ -174,7 +174,7 @@ "responseCode":204 }, "input":{"shape":"DeleteBucketReplicationRequest"}, - "documentation":"

    Deletes the replication configuration from the bucket. For information about replication configuration, see Cross-Region Replication (CRR) in the Amazon S3 Developer Guide.

    " + "documentation":"

    Deletes the replication configuration from the bucket.

    To use this operation, you must have permissions to perform the s3:PutReplicationConfiguration action. The bucket owner has these permissions by default and can grant it to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    It can take a while for the deletion of a replication configuration to fully propagate.

    For information about replication configuration, see Replication in the Amazon S3 Developer Guide.

    The following operations are related to DeleteBucketReplication:

    " }, "DeleteBucketTagging":{ "name":"DeleteBucketTagging", @@ -185,7 +185,7 @@ }, "input":{"shape":"DeleteBucketTaggingRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEtagging.html", - "documentation":"

    Deletes the tags from the bucket.

    " + "documentation":"

    Deletes the tags from the bucket.

    To use this operation, you must have permission to perform the s3:PutBucketTagging action. By default, the bucket owner has this permission and can grant this permission to others.

    The following operations are related to DeleteBucketTagging:

    " }, "DeleteBucketWebsite":{ "name":"DeleteBucketWebsite", @@ -196,7 +196,7 @@ }, "input":{"shape":"DeleteBucketWebsiteRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketDELETEwebsite.html", - "documentation":"

    This operation removes the website configuration from the bucket.

    " + "documentation":"

    This operation removes the website configuration for a bucket. Amazon S3 returns a 200 OK response upon successfully deleting a website configuration on the specified bucket. You will get a 200 OK response if the website configuration you are trying to delete does not exist on the bucket. Amazon S3 returns a 404 response if the bucket specified in the request does not exist.

    This DELETE operation requires the S3:DeleteBucketWebsite permission. By default, only the bucket owner can delete the website configuration attached to a bucket. However, bucket owners can grant other users permission to delete the website configuration by writing a bucket policy granting them the S3:DeleteBucketWebsite permission.

    For more information about hosting websites, see Hosting Websites on Amazon S3.

    The following operations are related to DeleteBucketWebsite:

    " }, "DeleteObject":{ "name":"DeleteObject", @@ -208,7 +208,7 @@ "input":{"shape":"DeleteObjectRequest"}, "output":{"shape":"DeleteObjectOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectDELETE.html", - "documentation":"

    Removes the null version (if there is one) of an object and inserts a delete marker, which becomes the latest version of the object. If there isn't a null version, Amazon S3 does not remove any objects.

    " + "documentation":"

    Removes the null version (if there is one) of an object and inserts a delete marker, which becomes the latest version of the object. If there isn't a null version, Amazon S3 does not remove any objects.

    To remove a specific version, you must be the bucket owner and you must use the version Id subresource. Using this subresource permanently deletes the version. If the object deleted is a delete marker, Amazon S3 sets the response header, x-amz-delete-marker, to true.

    If the object you want to delete is in a bucket where the bucket versioning configuration is MFA Delete enabled, you must include the x-amz-mfa request header in the DELETE versionId request. Requests that include x-amz-mfa must use HTTPS.

    For more information about MFA Delete, see Using MFA Delete. To see sample requests that use versioning, see Sample Request.

    You can delete objects by explicitly calling the DELETE Object API or configure its lifecycle (PutBucketLifecycle) to enable Amazon S3 to remove them for you. If you want to block users or accounts from removing or deleting objects from your bucket, you must deny them the s3:DeleteObject, s3:DeleteObjectVersion, and s3:PutLifeCycleConfiguration actions.

    The following operation is related to DeleteObject:

    " }, "DeleteObjectTagging":{ "name":"DeleteObjectTagging", @@ -219,7 +219,7 @@ }, "input":{"shape":"DeleteObjectTaggingRequest"}, "output":{"shape":"DeleteObjectTaggingOutput"}, - "documentation":"

    Removes the tag-set from an existing object.

    " + "documentation":"

    Removes the entire tag set from the specified object. For more information about managing object tags, see Object Tagging.

    To use this operation, you must have permission to perform the s3:DeleteObjectTagging action.

    To delete tags of a specific object version, add the versionId query parameter in the request. You will need permission for the s3:DeleteObjectVersionTagging action.

    The following operations are related to DeleteBucketMetricsConfiguration:

    " }, "DeleteObjects":{ "name":"DeleteObjects", @@ -230,7 +230,7 @@ "input":{"shape":"DeleteObjectsRequest"}, "output":{"shape":"DeleteObjectsOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/multiobjectdeleteapi.html", - "documentation":"

    This operation enables you to delete multiple objects from a bucket using a single HTTP request. You may specify up to 1000 keys.

    ", + "documentation":"

    This operation enables you to delete multiple objects from a bucket using a single HTTP request. If you know the object keys that you want to delete, then this operation provides a suitable alternative to sending individual delete requests, reducing per-request overhead.

    The request contains a list of up to 1000 keys that you want to delete. In the XML, you provide the object key names, and optionally, version IDs if you want to delete a specific version of the object from a versioning-enabled bucket. For each key, Amazon S3 performs a delete operation and returns the result of that delete, success, or failure, in the response. Note that if the object specified in the request is not found, Amazon S3 returns the result as deleted.

    The operation supports two modes for the response: verbose and quiet. By default, the operation uses verbose mode in which the response includes the result of deletion of each key in your request. In quiet mode the response includes only keys where the delete operation encountered an error. For a successful deletion, the operation does not return any information about the delete in the response body.

    When performing this operation on an MFA Delete enabled bucket, that attempts to delete any versioned objects, you must include an MFA token. If you do not provide one, the entire request will fail, even if there are non-versioned objects you are trying to delete. If you provide an invalid token, whether there are versioned keys in the request or not, the entire Multi-Object Delete request will fail. For information about MFA Delete, see MFA Delete.

    Finally, the Content-MD5 header is required for all Multi-Object Delete requests. Amazon S3 uses the header value to ensure that your request body has not been altered in transit.

    The following operations are related to DeleteObjects:

    ", "alias":"DeleteMultipleObjects" }, "DeletePublicAccessBlock":{ @@ -241,7 +241,7 @@ "responseCode":204 }, "input":{"shape":"DeletePublicAccessBlockRequest"}, - "documentation":"

    Removes the PublicAccessBlock configuration from an Amazon S3 bucket.

    " + "documentation":"

    Removes the PublicAccessBlock configuration for an Amazon S3 bucket. To use this operation, you must have the s3:PutBucketPublicAccessBlock permission. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    The following operations are related to DeleteBucketMetricsConfiguration:

    " }, "GetBucketAccelerateConfiguration":{ "name":"GetBucketAccelerateConfiguration", @@ -251,7 +251,7 @@ }, "input":{"shape":"GetBucketAccelerateConfigurationRequest"}, "output":{"shape":"GetBucketAccelerateConfigurationOutput"}, - "documentation":"

    Returns the accelerate configuration of a bucket.

    " + "documentation":"

    This implementation of the GET operation uses the accelerate subresource to return the Transfer Acceleration state of a bucket, which is either Enabled or Suspended. Amazon S3 Transfer Acceleration is a bucket-level feature that enables you to perform faster data transfers to and from Amazon S3.

    To use this operation, you must have permission to perform the s3:GetAccelerateConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    You set the Transfer Acceleration state of an existing bucket to Enabled or Suspended by using the PutBucketAccelerateConfiguration operation.

    A GET accelerate request does not return a state value for a bucket that has no transfer acceleration state. A bucket has no Transfer Acceleration state if a state has never been set on the bucket.

    For more information about transfer acceleration, see Transfer Acceleration in the Amazon Simple Storage Service Developer Guide.

    Related Resources

    " }, "GetBucketAcl":{ "name":"GetBucketAcl", @@ -262,7 +262,7 @@ "input":{"shape":"GetBucketAclRequest"}, "output":{"shape":"GetBucketAclOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETacl.html", - "documentation":"

    Gets the access control policy for the bucket.

    " + "documentation":"

    This implementation of the GET operation uses the acl subresource to return the access control list (ACL) of a bucket. To use GET to return the ACL of the bucket, you must have READ_ACP access to the bucket. If READ_ACP permission is granted to the anonymous user, you can return the ACL of the bucket without using an authorization header.

    Related Resources

    " }, "GetBucketAnalyticsConfiguration":{ "name":"GetBucketAnalyticsConfiguration", @@ -272,7 +272,7 @@ }, "input":{"shape":"GetBucketAnalyticsConfigurationRequest"}, "output":{"shape":"GetBucketAnalyticsConfigurationOutput"}, - "documentation":"

    Gets an analytics configuration for the bucket (specified by the analytics configuration ID).

    " + "documentation":"

    This implementation of the GET operation returns an analytics configuration (identified by the analytics configuration ID) from the bucket.

    To use this operation, you must have permissions to perform the s3:GetAnalyticsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class Analysis in the Amazon Simple Storage Service Developer Guide.

    Related Resources

    " }, "GetBucketCors":{ "name":"GetBucketCors", @@ -283,7 +283,7 @@ "input":{"shape":"GetBucketCorsRequest"}, "output":{"shape":"GetBucketCorsOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETcors.html", - "documentation":"

    Returns the CORS configuration for the bucket.

    " + "documentation":"

    Returns the cors configuration information set for the bucket.

    To use this operation, you must have permission to perform the s3:GetBucketCORS action. By default, the bucket owner has this permission and can grant it to others.

    For more information about cors, see Enabling Cross-Origin Resource Sharing.

    The following operations are related to GetBucketCors:

    " }, "GetBucketEncryption":{ "name":"GetBucketEncryption", @@ -293,7 +293,7 @@ }, "input":{"shape":"GetBucketEncryptionRequest"}, "output":{"shape":"GetBucketEncryptionOutput"}, - "documentation":"

    Returns the server-side encryption configuration of a bucket.

    " + "documentation":"

    Returns the default encryption configuration for an Amazon S3 bucket. For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption.

    To use this operation, you must have permission to perform the s3:GetEncryptionConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    The following operations are related to GetBucketEncryption:

    " }, "GetBucketInventoryConfiguration":{ "name":"GetBucketInventoryConfiguration", @@ -303,7 +303,7 @@ }, "input":{"shape":"GetBucketInventoryConfigurationRequest"}, "output":{"shape":"GetBucketInventoryConfigurationOutput"}, - "documentation":"

    Returns an inventory configuration (identified by the inventory ID) from the bucket.

    " + "documentation":"

    Returns an inventory configuration (identified by the inventory configuration ID) from the bucket.

    To use this operation, you must have permissions to perform the s3:GetInventoryConfiguration action. The bucket owner has this permission by default and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

    The following operations are related to GetBucketInventoryConfiguration:

    " }, "GetBucketLifecycle":{ "name":"GetBucketLifecycle", @@ -314,7 +314,7 @@ "input":{"shape":"GetBucketLifecycleRequest"}, "output":{"shape":"GetBucketLifecycleOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETlifecycle.html", - "documentation":"

    No longer used, see the GetBucketLifecycleConfiguration operation.

    ", + "documentation":"

    For an updated version of this API, see GetBucketLifecycleConfiguration. If you configured a bucket lifecycle using the filter element, you should see the updated version of this topic. This topic is provided for backward compatibility.

    Returns the lifecycle configuration information set on the bucket. For information about lifecycle configuration, see Object Lifecycle Management.

    To use this operation, you must have permission to perform the s3:GetLifecycleConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    GetBucketLifecycle has the following special error:

    • Error code: NoSuchLifecycleConfiguration

      • Description: The lifecycle configuration does not exist.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

    The following operations are related to GetBucketLifecycle:

    ", "deprecated":true }, "GetBucketLifecycleConfiguration":{ @@ -325,7 +325,7 @@ }, "input":{"shape":"GetBucketLifecycleConfigurationRequest"}, "output":{"shape":"GetBucketLifecycleConfigurationOutput"}, - "documentation":"

    Returns the lifecycle configuration information set on the bucket.

    " + "documentation":"

    Bucket lifecycle configuration now supports specifying a lifecycle rule using an object key name prefix, one or more object tags, or a combination of both. Accordingly, this section describes the latest API. The response describes the new filter element that you can use to specify a filter to select a subset of objects to which the rule applies. If you are still using previous version of the lifecycle configuration, it works. For the earlier API description, see GetBucketLifecycle.

    Returns the lifecycle configuration information set on the bucket. For information about lifecycle configuration, see Object Lifecycle Management.

    To use this operation, you must have permission to perform the s3:GetLifecycleConfiguration action. The bucket owner has this permission, by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    GetBucketLifecycleConfiguration has the following special error:

    • Error code: NoSuchLifecycleConfiguration

      • Description: The lifecycle configuration does not exist.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

    The following operations are related to DeleteBucketMetricsConfiguration:

    " }, "GetBucketLocation":{ "name":"GetBucketLocation", @@ -336,7 +336,7 @@ "input":{"shape":"GetBucketLocationRequest"}, "output":{"shape":"GetBucketLocationOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETlocation.html", - "documentation":"

    Returns the region the bucket resides in.

    " + "documentation":"

    Returns the Region the bucket resides in. You set the bucket's Region using the LocationConstraint request parameter in a CreateBucket request. For more information, see CreateBucket.

    To use this implementation of the operation, you must be the bucket owner.

    The following operations are related to GetBucketLocation:

    " }, "GetBucketLogging":{ "name":"GetBucketLogging", @@ -347,7 +347,7 @@ "input":{"shape":"GetBucketLoggingRequest"}, "output":{"shape":"GetBucketLoggingOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETlogging.html", - "documentation":"

    Returns the logging status of a bucket and the permissions users have to view and modify that status. To use GET, you must be the bucket owner.

    " + "documentation":"

    Returns the logging status of a bucket and the permissions users have to view and modify that status. To use GET, you must be the bucket owner.

    The following operations are related to GetBucketLogging:

    " }, "GetBucketMetricsConfiguration":{ "name":"GetBucketMetricsConfiguration", @@ -357,7 +357,7 @@ }, "input":{"shape":"GetBucketMetricsConfigurationRequest"}, "output":{"shape":"GetBucketMetricsConfigurationOutput"}, - "documentation":"

    Gets a metrics configuration (specified by the metrics configuration ID) from the bucket.

    " + "documentation":"

    Gets a metrics configuration (specified by the metrics configuration ID) from the bucket. Note that this doesn't include the daily storage metrics.

    To use this operation, you must have permissions to perform the s3:GetMetricsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon CloudWatch.

    The following operations are related to GetBucketMetricsConfiguration:

    " }, "GetBucketNotification":{ "name":"GetBucketNotification", @@ -368,7 +368,7 @@ "input":{"shape":"GetBucketNotificationConfigurationRequest"}, "output":{"shape":"NotificationConfigurationDeprecated"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETnotification.html", - "documentation":"

    No longer used, see the GetBucketNotificationConfiguration operation.

    ", + "documentation":"

    No longer used, see GetBucketNotificationConfiguration.

    ", "deprecated":true }, "GetBucketNotificationConfiguration":{ @@ -379,7 +379,7 @@ }, "input":{"shape":"GetBucketNotificationConfigurationRequest"}, "output":{"shape":"NotificationConfiguration"}, - "documentation":"

    Returns the notification configuration of a bucket.

    " + "documentation":"

    Returns the notification configuration of a bucket.

    If notifications are not enabled on the bucket, the operation returns an empty NotificationConfiguration element.

    By default, you must be the bucket owner to read the notification configuration of a bucket. However, the bucket owner can use a bucket policy to grant permission to other users to read this configuration with the s3:GetBucketNotification permission.

    For more information about setting and reading the notification configuration on a bucket, see Setting Up Notification of Bucket Events. For more information about bucket policies, see Using Bucket Policies.

    The following operation is related to GetBucketNotification:

    " }, "GetBucketPolicy":{ "name":"GetBucketPolicy", @@ -390,7 +390,7 @@ "input":{"shape":"GetBucketPolicyRequest"}, "output":{"shape":"GetBucketPolicyOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETpolicy.html", - "documentation":"

    Returns the policy of a specified bucket.

    " + "documentation":"

    Returns the policy of a specified bucket. If you are using an identity other than the root user of the AWS account that owns the bucket, the calling identity must have the GetBucketPolicy permissions on the specified bucket and belong to the bucket owner's account in order to use this operation.

    If you don't have GetBucketPolicy permissions, Amazon S3 returns a 403 Access Denied error. If you have the correct permissions, but you're not using an identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not Allowed error.

    As a security precaution, the root user of the AWS account that owns a bucket can always use this operation, even if the policy explicitly denies the root user the ability to perform this action.

    For more information about bucket policies, see Using Bucket Policies and User Policies.

    The following operation is related to GetBucketPolicy:

    " }, "GetBucketPolicyStatus":{ "name":"GetBucketPolicyStatus", @@ -400,7 +400,7 @@ }, "input":{"shape":"GetBucketPolicyStatusRequest"}, "output":{"shape":"GetBucketPolicyStatusOutput"}, - "documentation":"

    Retrieves the policy status for an Amazon S3 bucket, indicating whether the bucket is public.

    " + "documentation":"

    Retrieves the policy status for an Amazon S3 bucket, indicating whether the bucket is public. In order to use this operation, you must have the s3:GetBucketPolicyStatus permission. For more information about Amazon S3 permissions, see Specifying Permissions in a Policy.

    For more information about when Amazon S3 considers a bucket public, see The Meaning of \"Public\".

    The following operations are related to GetBucketPolicyStatus:

    " }, "GetBucketReplication":{ "name":"GetBucketReplication", @@ -410,7 +410,7 @@ }, "input":{"shape":"GetBucketReplicationRequest"}, "output":{"shape":"GetBucketReplicationOutput"}, - "documentation":"

    Returns the replication configuration of a bucket.

    It can take a while to propagate the put or delete a replication configuration to all Amazon S3 systems. Therefore, a get request soon after put or delete can return a wrong result.

    " + "documentation":"

    Returns the replication configuration of a bucket.

    It can take a while to propagate the put or delete a replication configuration to all Amazon S3 systems. Therefore, a get request soon after put or delete can return a wrong result.

    For information about replication configuration, see Replication in the Amazon Simple Storage Service Developer Guide.

    This operation requires permissions for the s3:GetReplicationConfiguration action. For more information about permissions, see Using Bucket Policies and User Policies.

    If you include the Filter element in a replication configuration, you must also include the DeleteMarkerReplication and Priority elements. The response also returns those elements.

    For information about GetBucketReplication errors, see ReplicationErrorCodeList

    The following operations are related to GetBucketReplication:

    " }, "GetBucketRequestPayment":{ "name":"GetBucketRequestPayment", @@ -421,7 +421,7 @@ "input":{"shape":"GetBucketRequestPaymentRequest"}, "output":{"shape":"GetBucketRequestPaymentOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTrequestPaymentGET.html", - "documentation":"

    Returns the request payment configuration of a bucket.

    " + "documentation":"

    Returns the request payment configuration of a bucket. To use this version of the operation, you must be the bucket owner. For more information, see Requester Pays Buckets.

    The following operations are related to GetBucketRequestPayment:

    " }, "GetBucketTagging":{ "name":"GetBucketTagging", @@ -432,7 +432,7 @@ "input":{"shape":"GetBucketTaggingRequest"}, "output":{"shape":"GetBucketTaggingOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETtagging.html", - "documentation":"

    Returns the tag set associated with the bucket.

    " + "documentation":"

    Returns the tag set associated with the bucket.

    To use this operation, you must have permission to perform the s3:GetBucketTagging action. By default, the bucket owner has this permission and can grant this permission to others.

    GetBucketTagging has the following special error:

    • Error code: NoSuchTagSetError

      • Description: There is no tag set associated with the bucket.

    The following operations are related to GetBucketTagging:

    " }, "GetBucketVersioning":{ "name":"GetBucketVersioning", @@ -443,7 +443,7 @@ "input":{"shape":"GetBucketVersioningRequest"}, "output":{"shape":"GetBucketVersioningOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETversioningStatus.html", - "documentation":"

    Returns the versioning state of a bucket.

    " + "documentation":"

    Returns the versioning state of a bucket.

    To retrieve the versioning state of a bucket, you must be the bucket owner.

    This implementation also returns the MFA Delete status of the versioning state. If the MFA Delete status is enabled, the bucket owner must use an authentication device to change the versioning state of the bucket.

    The following operations are related to GetBucketVersioning:

    " }, "GetBucketWebsite":{ "name":"GetBucketWebsite", @@ -454,7 +454,7 @@ "input":{"shape":"GetBucketWebsiteRequest"}, "output":{"shape":"GetBucketWebsiteOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETwebsite.html", - "documentation":"

    Returns the website configuration for a bucket.

    " + "documentation":"

    Returns the website configuration for a bucket. To host website on Amazon S3, you can configure a bucket as website by adding a website configuration. For more information about hosting websites, see Hosting Websites on Amazon S3.

    This GET operation requires the S3:GetBucketWebsite permission. By default, only the bucket owner can read the bucket website configuration. However, bucket owners can allow other users to read the website configuration by writing a bucket policy granting them the S3:GetBucketWebsite permission.

    The following operations are related to DeleteBucketWebsite:

    " }, "GetObject":{ "name":"GetObject", @@ -468,7 +468,7 @@ {"shape":"NoSuchKey"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGET.html", - "documentation":"

    Retrieves objects from Amazon S3.

    " + "documentation":"

    Retrieves objects from Amazon S3. To use GET, you must have READ access to the object. If you grant READ access to the anonymous user, you can return the object without using an authorization header.

    An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer file system. You can, however, create a logical hierarchy by using object key names that imply a folder structure. For example, instead of naming an object sample.jpg, you can name it photos/2006/February/sample.jpg.

    To get an object from such a logical hierarchy, specify the full key name for the object in the GET operation. For a virtual hosted-style request example, if you have the object photos/2006/February/sample.jpg, specify the resource as /photos/2006/February/sample.jpg. For a path-style request example, if you have the object photos/2006/February/sample.jpg in the bucket named examplebucket, specify the resource as /examplebucket/photos/2006/February/sample.jpg. For more information about request types, see HTTP Host Header Bucket Specification.

    To distribute large files to many people, you can save bandwidth costs by using BitTorrent. For more information, see Amazon S3 Torrent. For more information about returning the ACL of an object, see GetObjectAcl.

    If the object you are retrieving is stored in the GLACIER or DEEP_ARCHIVE storage classes, before you can retrieve the object you must first restore a copy using . Otherwise, this operation returns an InvalidObjectStateError error. For information about restoring archived objects, see Restoring Archived Objects.

    Encryption request headers, like x-amz-server-side-encryption, should not be sent for GET requests if your object uses server-side encryption with CMKs stored in AWS KMS (SSE-KMS) or server-side encryption with Amazon S3–managed encryption keys (SSE-S3). If your object does use these types of keys, you’ll get an HTTP 400 BadRequest error.

    If you encrypt an object by using server-side encryption with customer-provided encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object, you must use the following headers:

    • x-amz-server-side​-encryption​-customer-algorithm

    • x-amz-server-side​-encryption​-customer-key

    • x-amz-server-side​-encryption​-customer-key-MD5

    For more information about SSE-C, see Server-Side Encryption (Using Customer-Provided Encryption Keys).

    Assuming you have permission to read object tags (permission for the s3:GetObjectVersionTagging action), the response also returns the x-amz-tagging-count header that provides the count of number of tags associated with the object. You can use GetObjectTagging to retrieve the tag set associated with an object.

    Permissions

    You need the s3:GetObject permission for this operation. For more information, see Specifying Permissions in a Policy. If the object you request does not exist, the error Amazon S3 returns depends on whether you also have the s3:ListBucket permission.

    • If you have the s3:ListBucket permission on the bucket, Amazon S3 will return an HTTP status code 404 (\"no such key\") error.

    • If you don’t have the s3:ListBucket permission, Amazon S3 will return an HTTP status code 403 (\"access denied\") error.

    Versioning

    By default, the GET operation returns the current version of an object. To return a different version, use the versionId subresource.

    If the current version of the object is a delete marker, Amazon S3 behaves as if the object was deleted and includes x-amz-delete-marker: true in the response.

    For more information about versioning, see PutBucketVersioning.

    Overriding Response Header Values

    There are times when you want to override certain response header values in a GET response. For example, you might override the Content-Disposition response header value in your GET request.

    You can override values for a set of response headers using the following query parameters. These response header values are sent only on a successful request, that is, when status code 200 OK is returned. The set of headers you can override using these parameters is a subset of the headers that Amazon S3 accepts when you create an object. The response headers that you can override for the GET response are Content-Type, Content-Language, Expires, Cache-Control, Content-Disposition, and Content-Encoding. To override these header values in the GET response, you use the following request parameters.

    You must sign the request, either using an Authorization header or a presigned URL, when using these parameters. They cannot be used with an unsigned (anonymous) request.

    • response-content-type

    • response-content-language

    • response-expires

    • response-cache-control

    • response-content-disposition

    • response-content-encoding

    Additional Considerations about Request Headers

    If both of the If-Match and If-Unmodified-Since headers are present in the request as follows: If-Match condition evaluates to true, and; If-Unmodified-Since condition evaluates to false; then, S3 returns 200 OK and the data requested.

    If both of the If-None-Match and If-Modified-Since headers are present in the request as follows: If-None-Match condition evaluates to false, and; If-Modified-Since condition evaluates to true; then, S3 returns 304 Not Modified response code.

    For more information about conditional requests, see RFC 7232.

    The following operations are related to GetObject:

    " }, "GetObjectAcl":{ "name":"GetObjectAcl", @@ -482,7 +482,7 @@ {"shape":"NoSuchKey"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGETacl.html", - "documentation":"

    Returns the access control list (ACL) of an object.

    " + "documentation":"

    Returns the access control list (ACL) of an object. To use this operation, you must have READ_ACP access to the object.

    Versioning

    By default, GET returns ACL information about the current version of an object. To return ACL information about a different version, use the versionId subresource.

    The following operations are related to GetObjectAcl:

    " }, "GetObjectLegalHold":{ "name":"GetObjectLegalHold", @@ -492,7 +492,7 @@ }, "input":{"shape":"GetObjectLegalHoldRequest"}, "output":{"shape":"GetObjectLegalHoldOutput"}, - "documentation":"

    Gets an object's current Legal Hold status.

    " + "documentation":"

    Gets an object's current Legal Hold status. For more information, see Locking Objects.

    " }, "GetObjectLockConfiguration":{ "name":"GetObjectLockConfiguration", @@ -502,7 +502,7 @@ }, "input":{"shape":"GetObjectLockConfigurationRequest"}, "output":{"shape":"GetObjectLockConfigurationOutput"}, - "documentation":"

    Gets the object lock configuration for a bucket. The rule specified in the object lock configuration will be applied by default to every new object placed in the specified bucket.

    " + "documentation":"

    Gets the Object Lock configuration for a bucket. The rule specified in the Object Lock configuration will be applied by default to every new object placed in the specified bucket. For more information, see Locking Objects.

    " }, "GetObjectRetention":{ "name":"GetObjectRetention", @@ -512,7 +512,7 @@ }, "input":{"shape":"GetObjectRetentionRequest"}, "output":{"shape":"GetObjectRetentionOutput"}, - "documentation":"

    Retrieves an object's retention settings.

    " + "documentation":"

    Retrieves an object's retention settings. For more information, see Locking Objects.

    " }, "GetObjectTagging":{ "name":"GetObjectTagging", @@ -522,7 +522,7 @@ }, "input":{"shape":"GetObjectTaggingRequest"}, "output":{"shape":"GetObjectTaggingOutput"}, - "documentation":"

    Returns the tag-set of an object.

    " + "documentation":"

    Returns the tag-set of an object. You send the GET request against the tagging subresource associated with the object.

    To use this operation, you must have permission to perform the s3:GetObjectTagging action. By default, the GET operation returns information about current version of an object. For a versioned bucket, you can have multiple versions of an object in your bucket. To retrieve tags of any other version, use the versionId query parameter. You also need permission for the s3:GetObjectVersionTagging action.

    By default, the bucket owner has this permission and can grant this permission to others.

    For information about the Amazon S3 object tagging feature, see Object Tagging.

    The following operation is related to GetObjectTagging:

    " }, "GetObjectTorrent":{ "name":"GetObjectTorrent", @@ -533,7 +533,7 @@ "input":{"shape":"GetObjectTorrentRequest"}, "output":{"shape":"GetObjectTorrentOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectGETtorrent.html", - "documentation":"

    Return torrent files from a bucket.

    " + "documentation":"

    Return torrent files from a bucket. BitTorrent can save you bandwidth when you're distributing large files. For more information about BitTorrent, see Amazon S3 Torrent.

    You can get torrent only for objects that are less than 5 GB in size and that are not encrypted using server-side encryption with customer-provided encryption key.

    To use GET, you must have READ access to the object.

    The following operation is related to GetObjectTorrent:

    " }, "GetPublicAccessBlock":{ "name":"GetPublicAccessBlock", @@ -543,7 +543,7 @@ }, "input":{"shape":"GetPublicAccessBlockRequest"}, "output":{"shape":"GetPublicAccessBlockOutput"}, - "documentation":"

    Retrieves the PublicAccessBlock configuration for an Amazon S3 bucket.

    " + "documentation":"

    Retrieves the PublicAccessBlock configuration for an Amazon S3 bucket. To use this operation, you must have the s3:GetBucketPublicAccessBlock permission. For more information about Amazon S3 permissions, see Specifying Permissions in a Policy.

    When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or an object, it checks the PublicAccessBlock configuration for both the bucket (or the bucket that contains the object) and the bucket owner's account. If the PublicAccessBlock settings are different between the bucket and the account, Amazon S3 uses the most restrictive combination of the bucket-level and account-level settings.

    For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

    The following operations are related to GetPublicAccessBlock:

    " }, "HeadBucket":{ "name":"HeadBucket", @@ -556,7 +556,7 @@ {"shape":"NoSuchBucket"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketHEAD.html", - "documentation":"

    This operation is useful to determine if a bucket exists and you have permission to access it.

    " + "documentation":"

    This operation is useful to determine if a bucket exists and you have permission to access it. The operation returns a 200 OK if the bucket exists and you have permission to access it. Otherwise, the operation might return responses such as 404 Not Found and 403 Forbidden.

    To use this operation, you must have permissions to perform the s3:ListBucket action. The bucket owner has this permission by default and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    " }, "HeadObject":{ "name":"HeadObject", @@ -570,7 +570,7 @@ {"shape":"NoSuchKey"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectHEAD.html", - "documentation":"

    The HEAD operation retrieves metadata from an object without returning the object itself. This operation is useful if you're only interested in an object's metadata. To use HEAD, you must have READ access to the object.

    " + "documentation":"

    The HEAD operation retrieves metadata from an object without returning the object itself. This operation is useful if you're only interested in an object's metadata. To use HEAD, you must have READ access to the object.

    A HEAD request has the same options as a GET operation on an object. The response is identical to the GET response except that there is no response body.

    If you encrypt an object by using server-side encryption with customer-provided encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the metadata from the object, you must use the following headers:

    • x-amz-server-side​-encryption​-customer-algorithm

    • x-amz-server-side​-encryption​-customer-key

    • x-amz-server-side​-encryption​-customer-key-MD5

    For more information about SSE-C, see Server-Side Encryption (Using Customer-Provided Encryption Keys).

    Encryption request headers, like x-amz-server-side-encryption, should not be sent for GET requests if your object uses server-side encryption with CMKs stored in AWS KMS (SSE-KMS) or server-side encryption with Amazon S3–managed encryption keys (SSE-S3). If your object does use these types of keys, you’ll get an HTTP 400 BadRequest error.

    Request headers are limited to 8 KB in size. For more information, see Common Request Headers.

    Consider the following when using request headers:

    • Consideration 1 – If both of the If-Match and If-Unmodified-Since headers are present in the request as follows:

      • If-Match condition evaluates to true, and;

      • If-Unmodified-Since condition evaluates to false;

      Then Amazon S3 returns 200 OK and the data requested.

    • Consideration 2 – If both of the If-None-Match and If-Modified-Since headers are present in the request as follows:

      • If-None-Match condition evaluates to false, and;

      • If-Modified-Since condition evaluates to true;

      Then Amazon S3 returns the 304 Not Modified response code.

    For more information about conditional requests, see RFC 7232.

    Permissions

    You need the s3:GetObject permission for this operation. For more information, see Specifying Permissions in a Policy. If the object you request does not exist, the error Amazon S3 returns depends on whether you also have the s3:ListBucket permission.

    • If you have the s3:ListBucket permission on the bucket, Amazon S3 returns an HTTP status code 404 (\"no such key\") error.

    • If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP status code 403 (\"access denied\") error.

    The following operation is related to HeadObject:

    " }, "ListBucketAnalyticsConfigurations":{ "name":"ListBucketAnalyticsConfigurations", @@ -580,7 +580,7 @@ }, "input":{"shape":"ListBucketAnalyticsConfigurationsRequest"}, "output":{"shape":"ListBucketAnalyticsConfigurationsOutput"}, - "documentation":"

    Lists the analytics configurations for the bucket.

    " + "documentation":"

    Lists the analytics configurations for the bucket. You can have up to 1,000 analytics configurations per bucket.

    This operation supports list pagination and does not return more than 100 configurations at a time. You should always check the IsTruncated element in the response. If there are no more configurations to list, IsTruncated is set to false. If there are more configurations to list, IsTruncated is set to true, and there will be a value in NextContinuationToken. You use the NextContinuationToken value to continue the pagination of the list by passing the value in continuation-token in the request to GET the next page.

    To use this operation, you must have permissions to perform the s3:GetAnalyticsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class Analysis.

    The following operations are related to ListBucketAnalyticsConfigurations:

    " }, "ListBucketInventoryConfigurations":{ "name":"ListBucketInventoryConfigurations", @@ -590,7 +590,7 @@ }, "input":{"shape":"ListBucketInventoryConfigurationsRequest"}, "output":{"shape":"ListBucketInventoryConfigurationsOutput"}, - "documentation":"

    Returns a list of inventory configurations for the bucket.

    " + "documentation":"

    Returns a list of inventory configurations for the bucket. You can have up to 1,000 analytics configurations per bucket.

    This operation supports list pagination and does not return more than 100 configurations at a time. Always check the IsTruncated element in the response. If there are no more configurations to list, IsTruncated is set to false. If there are more configurations to list, IsTruncated is set to true, and there is a value in NextContinuationToken. You use the NextContinuationToken value to continue the pagination of the list by passing the value in continuation-token in the request to GET the next page.

    To use this operation, you must have permissions to perform the s3:GetInventoryConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about the Amazon S3 inventory feature, see Amazon S3 Inventory

    The following operations are related to ListBucketInventoryConfigurations:

    " }, "ListBucketMetricsConfigurations":{ "name":"ListBucketMetricsConfigurations", @@ -600,7 +600,7 @@ }, "input":{"shape":"ListBucketMetricsConfigurationsRequest"}, "output":{"shape":"ListBucketMetricsConfigurationsOutput"}, - "documentation":"

    Lists the metrics configurations for the bucket.

    " + "documentation":"

    Lists the metrics configurations for the bucket. The metrics configurations are only for the request metrics of the bucket and do not provide information on daily storage metrics. You can have up to 1,000 configurations per bucket.

    This operation supports list pagination and does not return more than 100 configurations at a time. Always check the IsTruncated element in the response. If there are no more configurations to list, IsTruncated is set to false. If there are more configurations to list, IsTruncated is set to true, and there is a value in NextContinuationToken. You use the NextContinuationToken value to continue the pagination of the list by passing the value in continuation-token in the request to GET the next page.

    To use this operation, you must have permissions to perform the s3:GetMetricsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For more information about metrics configurations and CloudWatch request metrics, see Monitoring Metrics with Amazon CloudWatch.

    The following operations are related to ListBucketMetricsConfigurations:

    " }, "ListBuckets":{ "name":"ListBuckets", @@ -622,7 +622,7 @@ "input":{"shape":"ListMultipartUploadsRequest"}, "output":{"shape":"ListMultipartUploadsOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadListMPUpload.html", - "documentation":"

    This operation lists in-progress multipart uploads.

    " + "documentation":"

    This operation lists in-progress multipart uploads. An in-progress multipart upload is a multipart upload that has been initiated using the Initiate Multipart Upload request, but has not yet been completed or aborted.

    This operation returns at most 1,000 multipart uploads in the response. 1,000 multipart uploads is the maximum number of uploads a response can include, which is also the default value. You can further limit the number of uploads in a response by specifying the max-uploads parameter in the response. If additional multipart uploads satisfy the list criteria, the response will contain an IsTruncated element with the value true. To list the additional multipart uploads, use the key-marker and upload-id-marker request parameters.

    In the response, the uploads are sorted by key. If your application has initiated more than one multipart upload using the same object key, then uploads in the response are first sorted by key. Additionally, uploads are sorted in ascending order within each key by the upload initiation time.

    For more information on multipart uploads, see Uploading Objects Using Multipart Upload.

    For information on permissions required to use the multipart upload API, see Multipart Upload API and Permissions.

    The following operations are related to ListMultipartUploads:

    " }, "ListObjectVersions":{ "name":"ListObjectVersions", @@ -633,7 +633,7 @@ "input":{"shape":"ListObjectVersionsRequest"}, "output":{"shape":"ListObjectVersionsOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGETVersion.html", - "documentation":"

    Returns metadata about all of the versions of objects in a bucket.

    ", + "documentation":"

    Returns metadata about all of the versions of objects in a bucket. You can also use request parameters as selection criteria to return metadata about a subset of all the object versions.

    A 200 OK response can contain valid or invalid XML. Make sure to design your application to parse the contents of the response and handle it appropriately.

    To use this operation, you must have READ access to the bucket.

    The following operations are related to ListObjectVersions:

    ", "alias":"GetBucketObjectVersions" }, "ListObjects":{ @@ -648,7 +648,7 @@ {"shape":"NoSuchBucket"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketGET.html", - "documentation":"

    Returns some or all (up to 1000) of the objects in a bucket. You can use the request parameters as selection criteria to return a subset of the objects in a bucket.

    ", + "documentation":"

    Returns some or all (up to 1,000) of the objects in a bucket. You can use the request parameters as selection criteria to return a subset of the objects in a bucket. A 200 OK response can contain valid or invalid XML. Be sure to design your application to parse the contents of the response and handle it appropriately.

    This API has been revised. We recommend that you use the newer version, ListObjectsV2, when developing applications. For backward compatibility, Amazon S3 continues to support ListObjects.

    The following operations are related to ListObjects:

    ", "alias":"GetBucket" }, "ListObjectsV2":{ @@ -662,7 +662,7 @@ "errors":[ {"shape":"NoSuchBucket"} ], - "documentation":"

    Returns some or all (up to 1000) of the objects in a bucket. You can use the request parameters as selection criteria to return a subset of the objects in a bucket. Note: ListObjectsV2 is the revised List Objects API and we recommend you use this revised API for new application development.

    " + "documentation":"

    Returns some or all (up to 1,000) of the objects in a bucket. You can use the request parameters as selection criteria to return a subset of the objects in a bucket. A 200 OK response can contain valid or invalid XML. Make sure to design your application to parse the contents of the response and handle it appropriately.

    To use this operation, you must have READ access to the bucket.

    To use this operation in an AWS Identity and Access Management (IAM) policy, you must have permissions to perform the s3:ListBucket action. The bucket owner has this permission by default and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    This section describes the latest revision of the API. We recommend that you use this revised API for application development. For backward compatibility, Amazon S3 continues to support the prior version of this API, ListObjects.

    To get a list of your buckets, see ListBuckets.

    The following operations are related to ListObjectsV2:

    " }, "ListParts":{ "name":"ListParts", @@ -673,7 +673,7 @@ "input":{"shape":"ListPartsRequest"}, "output":{"shape":"ListPartsOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadListParts.html", - "documentation":"

    Lists the parts that have been uploaded for a specific multipart upload.

    " + "documentation":"

    Lists the parts that have been uploaded for a specific multipart upload. This operation must include the upload ID, which you obtain by sending the initiate multipart upload request (see CreateMultipartUpload). This request returns a maximum of 1,000 uploaded parts. The default number of parts returned is 1,000 parts. You can restrict the number of parts returned by specifying the max-parts request parameter. If your multipart upload consists of more than 1,000 parts, the response returns an IsTruncated field with the value of true, and a NextPartNumberMarker element. In subsequent ListParts requests you can include the part-number-marker query string parameter and set its value to the NextPartNumberMarker field value from the previous response.

    For more information on multipart uploads, see Uploading Objects Using Multipart Upload.

    For information on permissions required to use the multipart upload API, see Multipart Upload API and Permissions.

    The following operations are related to ListParts:

    " }, "PutBucketAccelerateConfiguration":{ "name":"PutBucketAccelerateConfiguration", @@ -682,7 +682,7 @@ "requestUri":"/{Bucket}?accelerate" }, "input":{"shape":"PutBucketAccelerateConfigurationRequest"}, - "documentation":"

    Sets the accelerate configuration of an existing bucket.

    " + "documentation":"

    Sets the accelerate configuration of an existing bucket. Amazon S3 Transfer Acceleration is a bucket-level feature that enables you to perform faster data transfers to Amazon S3.

    To use this operation, you must have permission to perform the s3:PutAccelerateConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    The Transfer Acceleration state of a bucket can be set to one of the following two values:

    • Enabled – Enables accelerated data transfers to the bucket.

    • Suspended – Disables accelerated data transfers to the bucket.

    The GetBucketAccelerateConfiguration operation returns the transfer acceleration state of a bucket.

    After setting the Transfer Acceleration state of a bucket to Enabled, it might take up to thirty minutes before the data transfer rates to the bucket increase.

    The name of the bucket used for Transfer Acceleration must be DNS-compliant and must not contain periods (\".\").

    For more information about transfer acceleration, see Transfer Acceleration.

    The following operations are related to PutBucketAccelerateConfiguration:

    " }, "PutBucketAcl":{ "name":"PutBucketAcl", @@ -692,7 +692,7 @@ }, "input":{"shape":"PutBucketAclRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTacl.html", - "documentation":"

    Sets the permissions on a bucket using access control lists (ACL).

    " + "documentation":"

    Sets the permissions on an existing bucket using access control lists (ACL). For more information, see Using ACLs. To set the ACL of a bucket, you must have WRITE_ACP permission.

    You can use one of the following two ways to set a bucket's permissions:

    • Specify the ACL in the request body

    • Specify permissions using request headers

    You cannot specify access permission using both the body and the request headers.

    Depending on your application needs, you may choose to set the ACL on a bucket using either the request body or the headers. For example, if you have an existing application that updates a bucket ACL using the request body, then you can continue to use that approach.

    Access Permissions

    You can set access permissions using one of the following methods:

    • Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set of grantees and permissions. Specify the canned ACL name as the value of x-amz-acl. If you use this header, you cannot use other access control-specific headers in your request. For more information, see Canned ACL.

    • Specify access permissions explicitly with the x-amz-grant-read, x-amz-grant-read-acp, x-amz-grant-write-acp, and x-amz-grant-full-control headers. When using these headers, you specify explicit access permissions and grantees (AWS accounts or Amazon S3 groups) who will receive the permission. If you use these ACL-specific headers, you cannot use the x-amz-acl header to set a canned ACL. These parameters map to the set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview.

      You specify each grantee as a type=value pair, where the type is one of the following:

      • emailAddress – if the value specified is the email address of an AWS account

      • id – if the value specified is the canonical user ID of an AWS account

      • uri – if you are granting permissions to a predefined group

      For example, the following x-amz-grant-write header grants create, overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and two AWS accounts identified by their email addresses.

      x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\", emailAddress=\"xyz@amazon.com\", emailAddress=\"abc@amazon.com\"

    You can use either a canned ACL or specify access permissions explicitly. You cannot do both.

    Grantee Values

    You can specify the person (grantee) to whom you're assigning access rights (using request elements) in the following ways:

    • By Email address:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"AmazonCustomerByEmail\"><EmailAddress><>Grantees@email.com<></EmailAddress>lt;/Grantee>

      The grantee is resolved to the CanonicalUser and, in a response to a GET Object acl request, appears as the CanonicalUser.

    • By the person's ID:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID><>ID<></ID><DisplayName><>GranteesEmail<></DisplayName> </Grantee>

      DisplayName is optional and ignored in the request

    • By URI:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI><>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<></URI></Grantee>

    Related Resources

    " }, "PutBucketAnalyticsConfiguration":{ "name":"PutBucketAnalyticsConfiguration", @@ -701,7 +701,7 @@ "requestUri":"/{Bucket}?analytics" }, "input":{"shape":"PutBucketAnalyticsConfigurationRequest"}, - "documentation":"

    Sets an analytics configuration for the bucket (specified by the analytics configuration ID).

    " + "documentation":"

    Sets an analytics configuration for the bucket (specified by the analytics configuration ID). You can have up to 1,000 analytics configurations per bucket.

    You can choose to have storage class analysis export analysis reports sent to a comma-separated values (CSV) flat file. See the DataExport request element. Reports are updated daily and are based on the object filters that you configure. When selecting data export, you specify a destination bucket and an optional destination prefix where the file is written. You can export the data to a destination bucket in a different account. However, the destination bucket must be in the same Region as the bucket that you are making the PUT analytics configuration to. For more information, see Amazon S3 Analytics – Storage Class Analysis.

    You must create a bucket policy on the destination bucket where the exported file is written to grant permissions to Amazon S3 to write objects to the bucket. For an example policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

    To use this operation, you must have permissions to perform the s3:PutAnalyticsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    Special Errors

      • HTTP Error: HTTP 400 Bad Request

      • Code: InvalidArgument

      • Cause: Invalid argument.

      • HTTP Error: HTTP 400 Bad Request

      • Code: TooManyConfigurations

      • Cause: You are attempting to create a new configuration but have already reached the 1,000-configuration limit.

      • HTTP Error: HTTP 403 Forbidden

      • Code: AccessDenied

      • Cause: You are not the owner of the specified bucket, or you do not have the s3:PutAnalyticsConfiguration bucket permission to set the configuration on the bucket.

    Related Resources

    " }, "PutBucketCors":{ "name":"PutBucketCors", @@ -711,7 +711,7 @@ }, "input":{"shape":"PutBucketCorsRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTcors.html", - "documentation":"

    Sets the CORS configuration for a bucket.

    " + "documentation":"

    Sets the cors configuration for your bucket. If the configuration exists, Amazon S3 replaces it.

    To use this operation, you must be allowed to perform the s3:PutBucketCORS action. By default, the bucket owner has this permission and can grant it to others.

    You set this configuration on a bucket so that the bucket can service cross-origin requests. For example, you might want to enable a request whose origin is http://www.example.com to access your Amazon S3 bucket at my.example.bucket.com by using the browser's XMLHttpRequest capability.

    To enable cross-origin resource sharing (CORS) on a bucket, you add the cors subresource to the bucket. The cors subresource is an XML document in which you configure rules that identify origins and the HTTP methods that can be executed on your bucket. The document is limited to 64 KB in size.

    When Amazon S3 receives a cross-origin request (or a pre-flight OPTIONS request) against a bucket, it evaluates the cors configuration on the bucket and uses the first CORSRule rule that matches the incoming browser request to enable a cross-origin request. For a rule to match, the following conditions must be met:

    • The request's Origin header must match AllowedOrigin elements.

    • The request method (for example, GET, PUT, HEAD, and so on) or the Access-Control-Request-Method header in case of a pre-flight OPTIONS request must be one of the AllowedMethod elements.

    • Every header specified in the Access-Control-Request-Headers request header of a pre-flight request must match an AllowedHeader element.

    For more information about CORS, go to Enabling Cross-Origin Resource Sharing in the Amazon Simple Storage Service Developer Guide.

    Related Resources

    " }, "PutBucketEncryption":{ "name":"PutBucketEncryption", @@ -720,7 +720,7 @@ "requestUri":"/{Bucket}?encryption" }, "input":{"shape":"PutBucketEncryptionRequest"}, - "documentation":"

    Creates a new server-side encryption configuration (or replaces an existing one, if present).

    " + "documentation":"

    This implementation of the PUT operation uses the encryption subresource to set the default encryption state of an existing bucket.

    This implementation of the PUT operation sets default encryption for a bucket using server-side encryption with Amazon S3-managed keys SSE-S3 or AWS KMS customer master keys (CMKs) (SSE-KMS).

    This operation requires AWS Signature Version 4. For more information, see Authenticating Requests (AWS Signature Version 4).

    To use this operation, you must have permissions to perform the s3:PutEncryptionConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    Related Resources

    " }, "PutBucketInventoryConfiguration":{ "name":"PutBucketInventoryConfiguration", @@ -729,7 +729,7 @@ "requestUri":"/{Bucket}?inventory" }, "input":{"shape":"PutBucketInventoryConfigurationRequest"}, - "documentation":"

    Adds an inventory configuration (identified by the inventory ID) from the bucket.

    " + "documentation":"

    This implementation of the PUT operation adds an inventory configuration (identified by the inventory ID) to the bucket. You can have up to 1,000 inventory configurations per bucket.

    Amazon S3 inventory generates inventories of the objects in the bucket on a daily or weekly basis, and the results are published to a flat file. The bucket that is inventoried is called the source bucket, and the bucket where the inventory flat file is stored is called the destination bucket. The destination bucket must be in the same AWS Region as the source bucket.

    When you configure an inventory for a source bucket, you specify the destination bucket where you want the inventory to be stored, and whether to generate the inventory daily or weekly. You can also configure what object metadata to include and whether to inventory all object versions or only current versions. For more information, see Amazon S3 Inventory in the Amazon Simple Storage Service Developer Guide.

    You must create a bucket policy on the destination bucket to grant permissions to Amazon S3 to write objects to the bucket in the defined location. For an example policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

    To use this operation, you must have permissions to perform the s3:PutInventoryConfiguration action. The bucket owner has this permission by default and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    Special Errors

    • HTTP 400 Bad Request Error

      • Code: InvalidArgument

      • Cause: Invalid Argument

    • HTTP 400 Bad Request Error

      • Code: TooManyConfigurations

      • Cause: You are attempting to create a new configuration but have already reached the 1,000-configuration limit.

    • HTTP 403 Forbidden Error

      • Code: AccessDenied

      • Cause: You are not the owner of the specified bucket, or you do not have the s3:PutInventoryConfiguration bucket permission to set the configuration on the bucket

    Related Resources

    " }, "PutBucketLifecycle":{ "name":"PutBucketLifecycle", @@ -739,7 +739,7 @@ }, "input":{"shape":"PutBucketLifecycleRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTlifecycle.html", - "documentation":"

    No longer used, see the PutBucketLifecycleConfiguration operation.

    ", + "documentation":"

    For an updated version of this API, see PutBucketLifecycleConfiguration. This version has been deprecated. Existing lifecycle configurations will work. For new lifecycle configurations, use the updated API.

    Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle configuration. For information about lifecycle configuration, see Object Lifecycle Management in the Amazon Simple Storage Service Developer Guide.

    By default, all Amazon S3 resources, including buckets, objects, and related subresources (for example, lifecycle configuration and website configuration) are private. Only the resource owner, the AWS account that created the resource, can access it. The resource owner can optionally grant access permissions to others by writing an access policy. For this operation, users must get the s3:PutLifecycleConfiguration permission.

    You can also explicitly deny permissions. Explicit denial also supersedes any other permissions. If you want to prevent users or accounts from removing or deleting objects from your bucket, you must deny them permissions for the following actions:

    • s3:DeleteObject

    • s3:DeleteObjectVersion

    • s3:PutLifecycleConfiguration

    For more information about permissions, see Managing Access Permissions to your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    For more examples of transitioning objects to storage classes such as STANDARD_IA or ONEZONE_IA, see Examples of Lifecycle Configuration.

    Related Resources

    ", "deprecated":true }, "PutBucketLifecycleConfiguration":{ @@ -749,7 +749,7 @@ "requestUri":"/{Bucket}?lifecycle" }, "input":{"shape":"PutBucketLifecycleConfigurationRequest"}, - "documentation":"

    Sets lifecycle configuration for your bucket. If a lifecycle configuration exists, it replaces it.

    " + "documentation":"

    Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle configuration. For information about lifecycle configuration, see Managing Access Permissions to Your Amazon S3 Resources.

    Bucket lifecycle configuration now supports specifying a lifecycle rule using an object key name prefix, one or more object tags, or a combination of both. Accordingly, this section describes the latest API. The previous version of the API supported filtering based only on an object key name prefix, which is supported for backward compatibility. For the related API description, see PutBucketLifecycle.

    Rules

    You specify the lifecycle configuration in your request body. The lifecycle configuration is specified as XML consisting of one or more rules. Each rule consists of the following:

    • Filter identifying a subset of objects to which the rule applies. The filter can be based on a key name prefix, object tags, or a combination of both.

    • Status whether the rule is in effect.

    • One or more lifecycle transition and expiration actions that you want Amazon S3 to perform on the objects identified by the filter. If the state of your bucket is versioning-enabled or versioning-suspended, you can have many versions of the same object (one current version and zero or more noncurrent versions). Amazon S3 provides predefined actions that you can specify for current and noncurrent object versions.

    For more information, see Object Lifecycle Management and Lifecycle Configuration Elements.

    Permissions

    By default, all Amazon S3 resources are private, including buckets, objects, and related subresources (for example, lifecycle configuration and website configuration). Only the resource owner (that is, the AWS account that created it) can access the resource. The resource owner can optionally grant access permissions to others by writing an access policy. For this operation, a user must get the s3:PutLifecycleConfiguration permission.

    You can also explicitly deny permissions. Explicit deny also supersedes any other permissions. If you want to block users or accounts from removing or deleting objects from your bucket, you must deny them permissions for the following actions:

    • s3:DeleteObject

    • s3:DeleteObjectVersion

    • s3:PutLifecycleConfiguration

    For more information about permissions, see Managing Access Permissions to Your Amazon S3 Resources.

    The following are related to PutBucketLifecycleConfiguration:

    " }, "PutBucketLogging":{ "name":"PutBucketLogging", @@ -759,7 +759,7 @@ }, "input":{"shape":"PutBucketLoggingRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTlogging.html", - "documentation":"

    Set the logging parameters for a bucket and to specify permissions for who can view and modify the logging parameters. To set the logging status of a bucket, you must be the bucket owner.

    " + "documentation":"

    Set the logging parameters for a bucket and to specify permissions for who can view and modify the logging parameters. All logs are saved to buckets in the same AWS Region as the source bucket. To set the logging status of a bucket, you must be the bucket owner.

    The bucket owner is automatically granted FULL_CONTROL to all logs. You use the Grantee request element to grant access to other people. The Permissions request element specifies the kind of access the grantee has to the logs.

    Grantee Values

    You can specify the person (grantee) to whom you're assigning access rights (using request elements) in the following ways:

    • By the person's ID:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID><>ID<></ID><DisplayName><>GranteesEmail<></DisplayName> </Grantee>

      DisplayName is optional and ignored in the request.

    • By Email address:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"AmazonCustomerByEmail\"><EmailAddress><>Grantees@email.com<></EmailAddress></Grantee>

      The grantee is resolved to the CanonicalUser and, in a response to a GET Object acl request, appears as the CanonicalUser.

    • By URI:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI><>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<></URI></Grantee>

    To enable logging, you use LoggingEnabled and its children request elements. To disable logging, you use an empty BucketLoggingStatus request element:

    <BucketLoggingStatus xmlns=\"http://doc.s3.amazonaws.com/2006-03-01\" />

    For more information about server access logging, see Server Access Logging.

    For more information about creating a bucket, see CreateBucket. For more information about returning the logging status of a bucket, see GetBucketLogging.

    The following operations are related to PutBucketLogging:

    " }, "PutBucketMetricsConfiguration":{ "name":"PutBucketMetricsConfiguration", @@ -768,7 +768,7 @@ "requestUri":"/{Bucket}?metrics" }, "input":{"shape":"PutBucketMetricsConfigurationRequest"}, - "documentation":"

    Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.

    " + "documentation":"

    Sets a metrics configuration (specified by the metrics configuration ID) for the bucket. You can have up to 1,000 metrics configurations per bucket. If you're updating an existing metrics configuration, note that this is a full replacement of the existing metrics configuration. If you don't include the elements you want to keep, they are erased.

    To use this operation, you must have permissions to perform the s3:PutMetricsConfiguration action. The bucket owner has this permission by default. The bucket owner can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon CloudWatch.

    The following operations are related to PutBucketMetricsConfiguration:

    GetBucketLifecycle has the following special error:

    • Error code: TooManyConfigurations

      • Description: You are attempting to create a new configuration but have already reached the 1,000-configuration limit.

      • HTTP Status Code: HTTP 400 Bad Request

    " }, "PutBucketNotification":{ "name":"PutBucketNotification", @@ -778,7 +778,7 @@ }, "input":{"shape":"PutBucketNotificationRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTnotification.html", - "documentation":"

    No longer used, see the PutBucketNotificationConfiguration operation.

    ", + "documentation":"

    No longer used, see the PutBucketNotificationConfiguration operation.

    ", "deprecated":true }, "PutBucketNotificationConfiguration":{ @@ -788,7 +788,7 @@ "requestUri":"/{Bucket}?notification" }, "input":{"shape":"PutBucketNotificationConfigurationRequest"}, - "documentation":"

    Enables notifications of specified events for a bucket.

    " + "documentation":"

    Enables notifications of specified events for a bucket. For more information about event notifications, see Configuring Event Notifications.

    Using this API, you can replace an existing notification configuration. The configuration is an XML file that defines the event types that you want Amazon S3 to publish and the destination where you want Amazon S3 to publish an event notification when it detects an event of the specified type.

    By default, your bucket has no event notifications configured. That is, the notification configuration will be an empty NotificationConfiguration.

    <NotificationConfiguration>

    </NotificationConfiguration>

    This operation replaces the existing notification configuration with the configuration you include in the request body.

    After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and that the bucket owner has permission to publish to it by sending a test notification. In the case of AWS Lambda destinations, Amazon S3 verifies that the Lambda function permissions grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information, see Configuring Notifications for Amazon S3 Events.

    You can disable notifications by adding the empty NotificationConfiguration element.

    By default, only the bucket owner can configure notifications on a bucket. However, bucket owners can use a bucket policy to grant permission to other users to set this configuration with s3:PutBucketNotification permission.

    The PUT notification is an atomic operation. For example, suppose your notification configuration includes SNS topic, SQS queue, and Lambda function configurations. When you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS topic. If the message fails, the entire PUT operation will fail, and Amazon S3 will not add the configuration to your bucket.

    Responses

    If the configuration in the request body includes only one TopicConfiguration specifying only the s3:ReducedRedundancyLostObject event type, the response will also include the x-amz-sns-test-message-id header containing the message ID of the test notification sent to the topic.

    The following operation is related to PutBucketNotificationConfiguration:

    " }, "PutBucketPolicy":{ "name":"PutBucketPolicy", @@ -798,7 +798,7 @@ }, "input":{"shape":"PutBucketPolicyRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTpolicy.html", - "documentation":"

    Applies an Amazon S3 bucket policy to an Amazon S3 bucket.

    " + "documentation":"

    Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than the root user of the AWS account that owns the bucket, the calling identity must have the PutBucketPolicy permissions on the specified bucket and belong to the bucket owner's account in order to use this operation.

    If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403 Access Denied error. If you have the correct permissions, but you're not using an identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not Allowed error.

    As a security precaution, the root user of the AWS account that owns a bucket can always use this operation, even if the policy explicitly denies the root user the ability to perform this action.

    For more information about bucket policies, see Using Bucket Policies and User Policies.

    The following operations are related to PutBucketPolicy:

    " }, "PutBucketReplication":{ "name":"PutBucketReplication", @@ -807,7 +807,7 @@ "requestUri":"/{Bucket}?replication" }, "input":{"shape":"PutBucketReplicationRequest"}, - "documentation":"

    Creates a replication configuration or replaces an existing one. For more information, see Cross-Region Replication (CRR) in the Amazon S3 Developer Guide.

    " + "documentation":"

    Creates a replication configuration or replaces an existing one. For more information, see Replication in the Amazon S3 Developer Guide.

    To perform this operation, the user or role performing the operation must have the iam:PassRole permission.

    Specify the replication configuration in the request body. In the replication configuration, you provide the name of the destination bucket where you want Amazon S3 to replicate objects, the IAM role that Amazon S3 can assume to replicate objects on your behalf, and other relevant information.

    A replication configuration must include at least one rule, and can contain a maximum of 1,000. Each rule identifies a subset of objects to replicate by filtering the objects in the source bucket. To choose additional subsets of objects to replicate, add a rule for each subset. All rules must specify the same destination bucket.

    To specify a subset of the objects in the source bucket to apply a replication rule to, add the Filter element as a child of the Rule element. You can filter objects based on an object key prefix, one or more object tags, or both. When you add the Filter element in the configuration, you must also add the following elements: DeleteMarkerReplication, Status, and Priority.

    For information about enabling versioning on a bucket, see Using Versioning.

    By default, a resource owner, in this case the AWS account that created the bucket, can perform this operation. The resource owner can also grant others permissions to perform the operation. For more information about permissions, see Specifying Permissions in a Policy and Managing Access Permissions to Your Amazon S3 Resources.

    Handling Replication of Encrypted Objects

    By default, Amazon S3 doesn't replicate objects that are stored at rest using server-side encryption with CMKs stored in AWS KMS. To replicate AWS KMS-encrypted objects, add the following: SourceSelectionCriteria, SseKmsEncryptedObjects, Status, EncryptionConfiguration, and ReplicaKmsKeyID. For information about replication configuration, see Replicating Objects Created with SSE Using CMKs stored in AWS KMS.

    For information on PutBucketReplication errors, see ReplicationErrorCodeList

    The following operations are related to PutBucketReplication:

    " }, "PutBucketRequestPayment":{ "name":"PutBucketRequestPayment", @@ -817,7 +817,7 @@ }, "input":{"shape":"PutBucketRequestPaymentRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTrequestPaymentPUT.html", - "documentation":"

    Sets the request payment configuration for a bucket. By default, the bucket owner pays for downloads from the bucket. This configuration parameter enables the bucket owner (only) to specify that the person requesting the download will be charged for the download. Documentation on requester pays buckets can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/RequesterPaysBuckets.html

    " + "documentation":"

    Sets the request payment configuration for a bucket. By default, the bucket owner pays for downloads from the bucket. This configuration parameter enables the bucket owner (only) to specify that the person requesting the download will be charged for the download. For more information, see Requester Pays Buckets.

    The following operations are related to PutBucketRequestPayment:

    " }, "PutBucketTagging":{ "name":"PutBucketTagging", @@ -827,7 +827,7 @@ }, "input":{"shape":"PutBucketTaggingRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTtagging.html", - "documentation":"

    Sets the tags for a bucket.

    " + "documentation":"

    Sets the tags for a bucket.

    Use tags to organize your AWS bill to reflect your own cost structure. To do this, sign up to get your AWS account bill with tag key values included. Then, to see the cost of combined resources, organize your billing information according to resources with the same tag key values. For example, you can tag several resources with a specific application name, and then organize your billing information to see the total cost of that application across several services. For more information, see Cost Allocation and Tagging.

    Within a bucket, if you add a tag that has the same key as an existing tag, the new value overwrites the old value. For more information, see Using Cost Allocation in Amazon S3 Bucket Tags.

    To use this operation, you must have permissions to perform the s3:PutBucketTagging action. The bucket owner has this permission by default and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources.

    PutBucketTagging has the following special errors:

    • Error code: InvalidTagError

    • Error code: MalformedXMLError

      • Description: The XML provided does not match the schema.

    • Error code: OperationAbortedError

      • Description: A conflicting conditional operation is currently in progress against this resource. Please try again.

    • Error code: InternalError

      • Description: The service was unable to apply the provided tag to the bucket.

    The following operations are related to PutBucketTagging:

    " }, "PutBucketVersioning":{ "name":"PutBucketVersioning", @@ -837,7 +837,7 @@ }, "input":{"shape":"PutBucketVersioningRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTVersioningStatus.html", - "documentation":"

    Sets the versioning state of an existing bucket. To set the versioning state, you must be the bucket owner.

    " + "documentation":"

    Sets the versioning state of an existing bucket. To set the versioning state, you must be the bucket owner.

    You can set the versioning state with one of the following values:

    Enabled—Enables versioning for the objects in the bucket. All objects added to the bucket receive a unique version ID.

    Suspended—Disables versioning for the objects in the bucket. All objects added to the bucket receive the version ID null.

    If the versioning state has never been set on a bucket, it has no versioning state; a GetBucketVersioning request does not return a versioning state value.

    If the bucket owner enables MFA Delete in the bucket versioning configuration, the bucket owner must include the x-amz-mfa request header and the Status and the MfaDelete request elements in a request to set the versioning state of the bucket.

    If you have an object expiration lifecycle policy in your non-versioned bucket and you want to maintain the same permanent delete behavior when you enable versioning, you must add a noncurrent expiration policy. The noncurrent expiration lifecycle policy will manage the deletes of the noncurrent object versions in the version-enabled bucket. (A version-enabled bucket maintains one current and zero or more noncurrent object versions.) For more information, see Lifecycle and Versioning.

    Related Resources

    " }, "PutBucketWebsite":{ "name":"PutBucketWebsite", @@ -847,7 +847,7 @@ }, "input":{"shape":"PutBucketWebsiteRequest"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTBucketPUTwebsite.html", - "documentation":"

    Set the website configuration for a bucket.

    " + "documentation":"

    Sets the configuration of the website that is specified in the website subresource. To configure a bucket as a website, you can add this subresource on the bucket with website configuration information such as the file name of the index document and any redirect rules. For more information, see Hosting Websites on Amazon S3.

    This PUT operation requires the S3:PutBucketWebsite permission. By default, only the bucket owner can configure the website attached to a bucket; however, bucket owners can allow other users to set the website configuration by writing a bucket policy that grants them the S3:PutBucketWebsite permission.

    To redirect all website requests sent to the bucket's website endpoint, you add a website configuration with the following elements. Because all requests are sent to another website, you don't need to provide index document name for the bucket.

    • WebsiteConfiguration

    • RedirectAllRequestsTo

    • HostName

    • Protocol

    If you want granular control over redirects, you can use the following elements to add routing rules that describe conditions for redirecting requests and information about the redirect destination. In this case, the website configuration must provide an index document for the bucket, because some requests might not be redirected.

    • WebsiteConfiguration

    • IndexDocument

    • Suffix

    • ErrorDocument

    • Key

    • RoutingRules

    • RoutingRule

    • Condition

    • HttpErrorCodeReturnedEquals

    • KeyPrefixEquals

    • Redirect

    • Protocol

    • HostName

    • ReplaceKeyPrefixWith

    • ReplaceKeyWith

    • HttpRedirectCode

    " }, "PutObject":{ "name":"PutObject", @@ -858,7 +858,7 @@ "input":{"shape":"PutObjectRequest"}, "output":{"shape":"PutObjectOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUT.html", - "documentation":"

    Adds an object to a bucket.

    " + "documentation":"

    Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object to it.

    Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the entire object to the bucket.

    Amazon S3 is a distributed system. If it receives multiple write requests for the same object simultaneously, it overwrites all but the last object written. Amazon S3 does not provide object locking; if you need this, make sure to build it into your application layer or use versioning instead.

    To ensure that data is not corrupted traversing the network, use the Content-MD5 header. When you use this header, Amazon S3 checks the object against the provided MD5 value and, if they do not match, returns an error. Additionally, you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to the calculated MD5 value.

    To configure your application to send the request headers before sending the request body, use the 100-continue HTTP status code. For PUT operations, this helps you avoid sending the message body if the message is rejected based on the headers (for example, because authentication fails or a redirect occurs). For more information on the 100-continue HTTP status code, see Section 8.2.3 of http://www.ietf.org/rfc/rfc2616.txt.

    You can optionally request server-side encryption. With server-side encryption, Amazon S3 encrypts your data as it writes it to disks in its data centers and decrypts the data when you access it. You have the option to provide your own encryption key or use AWS managed encryption keys. For more information, see Using Server-Side Encryption.

    Access Permissions

    You can optionally specify the accounts or groups that should be granted specific permissions on the new object. There are two ways to grant the permissions using the request headers:

    • Specify a canned ACL with the x-amz-acl request header. For more information, see Canned ACL.

    • Specify access permissions explicitly with the x-amz-grant-read, x-amz-grant-read-acp, x-amz-grant-write-acp, and x-amz-grant-full-control headers. These parameters map to the set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview.

    You can use either a canned ACL or specify access permissions explicitly. You cannot do both.

    Server-Side- Encryption-Specific Request Headers

    You can optionally tell Amazon S3 to encrypt data at rest using server-side encryption. Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it writes it to disks in its data centers and decrypts it when you access it. The option you use depends on whether you want to use AWS managed encryption keys or provide your own encryption key.

    • Use encryption keys managed by Amazon S3 or customer master keys (CMKs) stored in AWS Key Management Service (AWS KMS) – If you want AWS to manage the keys used to encrypt data, specify the following headers in the request.

      • x-amz-server-side​-encryption

      • x-amz-server-side-encryption-aws-kms-key-id

      • x-amz-server-side-encryption-context

      If you specify x-amz-server-side-encryption:aws:kms, but don't provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the AWS managed CMK in AWS KMS to protect the data. If you want to use a customer managed AWS KMS CMK, you must provide the x-amz-server-side-encryption-aws-kms-key-id of the symmetric customer managed CMK. Amazon S3 only supports symmetric CMKs and not asymmetric CMKs. For more information, see Using Symmetric and Asymmetric Keys in the AWS Key Management Service Developer Guide.

      All GET and PUT requests for an object protected by AWS KMS fail if you don't make them with SSL or by using SigV4.

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in AWS.

    • Use customer-provided encryption keys – If you want to manage your own encryption keys, provide all the following headers in the request.

      • x-amz-server-side​-encryption​-customer-algorithm

      • x-amz-server-side​-encryption​-customer-key

      • x-amz-server-side​-encryption​-customer-key-MD5

      For more information about server-side encryption with CMKs stored in KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in AWS.

    Access-Control-List (ACL)-Specific Request Headers

    You also can use the following access control–related headers with this operation. By default, all objects are private. Only the owner has full access control. When adding a new object, you can grant permissions to individual AWS accounts or to predefined groups defined by Amazon S3. These permissions are then added to the Access Control List (ACL) on the object. For more information, see Using ACLs. With this operation, you can grant access permissions using one of the following two methods:

    • Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set of grantees and permissions. For more information, see Canned ACL.

    • Specify access permissions explicitly — To explicitly grant access permissions to specific AWS accounts or groups, use the following headers. Each header maps to specific permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview. In the header, you specify a list of grantees who get the specific permission. To grant permissions explicitly use:

      • x-amz-grant-read

      • x-amz-grant-write

      • x-amz-grant-read-acp

      • x-amz-grant-write-acp

      • x-amz-grant-full-control

      You specify each grantee as a type=value pair, where the type is one of the following:

      • emailAddress – if the value specified is the email address of an AWS account

        Using email addresses to specify a grantee is only supported in the following AWS Regions:

        • US East (N. Virginia)

        • US West (N. California)

        • US West (Oregon)

        • Asia Pacific (Singapore)

        • Asia Pacific (Sydney)

        • Asia Pacific (Tokyo)

        • EU (Ireland)

        • South America (São Paulo)

        For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the AWS General Reference

      • id – if the value specified is the canonical user ID of an AWS account

      • uri – if you are granting permissions to a predefined group

      For example, the following x-amz-grant-read header grants the AWS accounts identified by email addresses permissions to read object data and its metadata:

      x-amz-grant-read: emailAddress=\"xyz@amazon.com\", emailAddress=\"abc@amazon.com\"

    Server-Side- Encryption-Specific Request Headers

    You can optionally tell Amazon S3 to encrypt data at rest using server-side encryption. Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it writes it to disks in its data centers and decrypts it when you access it. The option you use depends on whether you want to use AWS-managed encryption keys or provide your own encryption key.

    • Use encryption keys managed by Amazon S3 or customer master keys (CMKs) stored in AWS Key Management Service (AWS KMS) – If you want AWS to manage the keys used to encrypt data, specify the following headers in the request.

      • x-amz-server-side​-encryption

      • x-amz-server-side-encryption-aws-kms-key-id

      • x-amz-server-side-encryption-context

      If you specify x-amz-server-side-encryption:aws:kms, but don't provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the AWS managed CMK in AWS KMS to protect the data. If you want to use a customer managed AWS KMS CMK, you must provide the x-amz-server-side-encryption-aws-kms-key-id of the symmetric customer managed CMK. Amazon S3 only supports symmetric CMKs and not asymmetric CMKs. For more information, see Using Symmetric and Asymmetric Keys in the AWS Key Management Service Developer Guide.

      All GET and PUT requests for an object protected by AWS KMS fail if you don't make them with SSL or by using SigV4.

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in AWS KMS.

    • Use customer-provided encryption keys – If you want to manage your own encryption keys, provide all the following headers in the request.

      If you use this feature, the ETag value that Amazon S3 returns in the response is not the MD5 of the object.

      • x-amz-server-side​-encryption​-customer-algorithm

      • x-amz-server-side​-encryption​-customer-key

      • x-amz-server-side​-encryption​-customer-key-MD5

      For more information about server-side encryption with CMKs stored in AWS KMS (SSE-KMS), see Protecting Data Using Server-Side Encryption with CMKs stored in AWS KMS.

    Storage Class Options

    By default, Amazon S3 uses the Standard storage class to store newly created objects. The Standard storage class provides high durability and high availability. You can specify other storage classes depending on the performance needs. For more information, see Storage Classes in the Amazon Simple Storage Service Developer Guide.

    Versioning

    If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID for the object being stored. Amazon S3 returns this ID in the response using the x-amz-version-id response header. If versioning is suspended, Amazon S3 always uses null as the version ID for the object stored. For more information about returning the versioning state of a bucket, see GetBucketVersioning. If you enable versioning for a bucket, when Amazon S3 receives multiple write requests for the same object simultaneously, it stores all of the objects.

    Related Resources

    " }, "PutObjectAcl":{ "name":"PutObjectAcl", @@ -872,7 +872,7 @@ {"shape":"NoSuchKey"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectPUTacl.html", - "documentation":"

    uses the acl subresource to set the access control list (ACL) permissions for an object that already exists in a bucket

    " + "documentation":"

    Uses the acl subresource to set the access control list (ACL) permissions for an object that already exists in a bucket. You must have WRITE_ACP permission to set the ACL of an object.

    Depending on your application needs, you can choose to set the ACL on an object using either the request body or the headers. For example, if you have an existing application that updates a bucket ACL using the request body, you can continue to use that approach.

    Access Permissions

    You can set access permissions using one of the following methods:

    • Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set of grantees and permissions. Specify the canned ACL name as the value of x-amz-acl. If you use this header, you cannot use other access control-specific headers in your request. For more information, see Canned ACL.

    • Specify access permissions explicitly with the x-amz-grant-read, x-amz-grant-read-acp, x-amz-grant-write-acp, and x-amz-grant-full-control headers. When using these headers, you specify explicit access permissions and grantees (AWS accounts or Amazon S3 groups) who will receive the permission. If you use these ACL-specific headers, you cannot use x-amz-acl header to set a canned ACL. These parameters map to the set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL) Overview.

      You specify each grantee as a type=value pair, where the type is one of the following:

      • emailAddress – if the value specified is the email address of an AWS account

      • id – if the value specified is the canonical user ID of an AWS account

      • uri – if you are granting permissions to a predefined group

      For example, the following x-amz-grant-read header grants list objects permission to the two AWS accounts identified by their email addresses.

      x-amz-grant-read: emailAddress=\"xyz@amazon.com\", emailAddress=\"abc@amazon.com\"

    You can use either a canned ACL or specify access permissions explicitly. You cannot do both.

    Grantee Values

    You can specify the person (grantee) to whom you're assigning access rights (using request elements) in the following ways:

    • By Email address:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"AmazonCustomerByEmail\"><EmailAddress><>Grantees@email.com<></EmailAddress>lt;/Grantee>

      The grantee is resolved to the CanonicalUser and, in a response to a GET Object acl request, appears as the CanonicalUser.

    • By the person's ID:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"CanonicalUser\"><ID><>ID<></ID><DisplayName><>GranteesEmail<></DisplayName> </Grantee>

      DisplayName is optional and ignored in the request.

    • By URI:

      <Grantee xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"Group\"><URI><>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<></URI></Grantee>

    Versioning

    The ACL of an object is set at the object version level. By default, PUT sets the ACL of the current version of an object. To set the ACL of a different version, use the versionId subresource.

    Related Resources

    " }, "PutObjectLegalHold":{ "name":"PutObjectLegalHold", @@ -882,7 +882,7 @@ }, "input":{"shape":"PutObjectLegalHoldRequest"}, "output":{"shape":"PutObjectLegalHoldOutput"}, - "documentation":"

    Applies a Legal Hold configuration to the specified object.

    " + "documentation":"

    Applies a Legal Hold configuration to the specified object.

    Related Resources

    " }, "PutObjectLockConfiguration":{ "name":"PutObjectLockConfiguration", @@ -892,7 +892,7 @@ }, "input":{"shape":"PutObjectLockConfigurationRequest"}, "output":{"shape":"PutObjectLockConfigurationOutput"}, - "documentation":"

    Places an object lock configuration on the specified bucket. The rule specified in the object lock configuration will be applied by default to every new object placed in the specified bucket.

    " + "documentation":"

    Places an Object Lock configuration on the specified bucket. The rule specified in the Object Lock configuration will be applied by default to every new object placed in the specified bucket.

    DefaultRetention requires either Days or Years. You can't specify both at the same time.

    Related Resources

    " }, "PutObjectRetention":{ "name":"PutObjectRetention", @@ -902,7 +902,7 @@ }, "input":{"shape":"PutObjectRetentionRequest"}, "output":{"shape":"PutObjectRetentionOutput"}, - "documentation":"

    Places an Object Retention configuration on an object.

    " + "documentation":"

    Places an Object Retention configuration on an object.

    Related Resources

    " }, "PutObjectTagging":{ "name":"PutObjectTagging", @@ -912,7 +912,7 @@ }, "input":{"shape":"PutObjectTaggingRequest"}, "output":{"shape":"PutObjectTaggingOutput"}, - "documentation":"

    Sets the supplied tag-set to an object that already exists in a bucket

    " + "documentation":"

    Sets the supplied tag-set to an object that already exists in a bucket

    A tag is a key-value pair. You can associate tags with an object by sending a PUT request against the tagging subresource that is associated with the object. You can retrieve tags by sending a GET request. For more information, see GetObjectTagging.

    For tagging-related restrictions related to characters and encodings, see Tag Restrictions. Note that Amazon S3 limits the maximum number of tags to 10 tags per object.

    To use this operation, you must have permission to perform the s3:PutObjectTagging action. By default, the bucket owner has this permission and can grant this permission to others.

    To put tags of any other version, use the versionId query parameter. You also need permission for the s3:PutObjectVersionTagging action.

    For information about the Amazon S3 object tagging feature, see Object Tagging.

    Special Errors

      • Code: InvalidTagError

      • Cause: The tag provided was not a valid tag. This error can occur if the tag did not pass input validation. For more information, see Object Tagging.

      • Code: MalformedXMLError

      • Cause: The XML provided does not match the schema.

      • Code: OperationAbortedError

      • Cause: A conflicting conditional operation is currently in progress against this resource. Please try again.

      • Code: InternalError

      • Cause: The service was unable to apply the provided tag to the object.

    Related Resources

    " }, "PutPublicAccessBlock":{ "name":"PutPublicAccessBlock", @@ -921,7 +921,7 @@ "requestUri":"/{Bucket}?publicAccessBlock" }, "input":{"shape":"PutPublicAccessBlockRequest"}, - "documentation":"

    Creates or modifies the PublicAccessBlock configuration for an Amazon S3 bucket.

    " + "documentation":"

    Creates or modifies the PublicAccessBlock configuration for an Amazon S3 bucket. To use this operation, you must have the s3:PutBucketPublicAccessBlock permission. For more information about Amazon S3 permissions, see Specifying Permissions in a Policy.

    When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or an object, it checks the PublicAccessBlock configuration for both the bucket (or the bucket that contains the object) and the bucket owner's account. If the PublicAccessBlock configurations are different between the bucket and the account, Amazon S3 uses the most restrictive combination of the bucket-level and account-level settings.

    For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

    Related Resources

    " }, "RestoreObject":{ "name":"RestoreObject", @@ -935,7 +935,7 @@ {"shape":"ObjectAlreadyInActiveTierError"} ], "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/RESTObjectRestore.html", - "documentation":"

    Restores an archived copy of an object back into Amazon S3

    ", + "documentation":"

    Restores an archived copy of an object back into Amazon S3

    This operation performs the following types of requests:

    • select - Perform a select query on an archived object

    • restore an archive - Restore an archived object

    To use this operation, you must have permissions to perform the s3:RestoreObject and s3:GetObject actions. The bucket owner has this permission by default and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3 Resources in the Amazon Simple Storage Service Developer Guide.

    Querying Archives with Select Requests

    You use a select type of request to perform SQL queries on archived objects. The archived objects that are being queried by the select request must be formatted as uncompressed comma-separated values (CSV) files. You can run queries and custom analytics on your archived data without having to restore your data to a hotter Amazon S3 tier. For an overview about select requests, see Querying Archived Objects in the Amazon Simple Storage Service Developer Guide.

    When making a select request, do the following:

    • Define an output location for the select query's output. This must be an Amazon S3 bucket in the same AWS Region as the bucket that contains the archive object that is being queried. The AWS account that initiates the job must have permissions to write to the S3 bucket. You can specify the storage class and encryption for the output objects stored in the bucket. For more information about output, see Querying Archived Objects in the Amazon Simple Storage Service Developer Guide.

      For more information about the S3 structure in the request body, see the following:

    • Define the SQL expression for the SELECT type of restoration for your query in the request body's SelectParameters structure. You can use expressions like the following examples.

      • The following expression returns all records from the specified object.

        SELECT * FROM Object

      • Assuming that you are not using any headers for data stored in the object, you can specify columns with positional headers.

        SELECT s._1, s._2 FROM Object s WHERE s._3 > 100

      • If you have headers and you set the fileHeaderInfo in the CSV structure in the request body to USE, you can specify headers in the query. (If you set the fileHeaderInfo field to IGNORE, the first row is skipped for the query.) You cannot mix ordinal positions with header column names.

        SELECT s.Id, s.FirstName, s.SSN FROM S3Object s

    For more information about using SQL with Glacier Select restore, see SQL Reference for Amazon S3 Select and Glacier Select in the Amazon Simple Storage Service Developer Guide.

    When making a select request, you can also do the following:

    • To expedite your queries, specify the Expedited tier. For more information about tiers, see \"Restoring Archives,\" later in this topic.

    • Specify details about the data serialization format of both the input object that is being queried and the serialization of the CSV-encoded query results.

    The following are additional important facts about the select feature:

    • The output results are new Amazon S3 objects. Unlike archive retrievals, they are stored until explicitly deleted-manually or through a lifecycle policy.

    • You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't deduplicate requests, so avoid issuing duplicate requests.

    • Amazon S3 accepts a select request even if the object has already been restored. A select request doesn’t return error response 409.

    Restoring Archives

    Objects in the GLACIER and DEEP_ARCHIVE storage classes are archived. To access an archived object, you must first initiate a restore request. This restores a temporary copy of the archived object. In a restore request, you specify the number of days that you want the restored copy to exist. After the specified period, Amazon S3 deletes the temporary copy but the object remains archived in the GLACIER or DEEP_ARCHIVE storage class that object was restored from.

    To restore a specific object version, you can provide a version ID. If you don't provide a version ID, Amazon S3 restores the current version.

    The time it takes restore jobs to finish depends on which storage class the object is being restored from and which data access tier you specify.

    When restoring an archived object (or using a select request), you can specify one of the following data access tier options in the Tier element of the request body:

    • Expedited - Expedited retrievals allow you to quickly access your data stored in the GLACIER storage class when occasional urgent requests for a subset of archives are required. For all but the largest archived objects (250 MB+), data accessed using Expedited retrievals are typically made available within 1–5 minutes. Provisioned capacity ensures that retrieval capacity for Expedited retrievals is available when you need it. Expedited retrievals and provisioned capacity are not available for the DEEP_ARCHIVE storage class.

    • Standard - Standard retrievals allow you to access any of your archived objects within several hours. This is the default option for the GLACIER and DEEP_ARCHIVE retrieval requests that do not specify the retrieval option. Standard retrievals typically complete within 3-5 hours from the GLACIER storage class and typically complete within 12 hours from the DEEP_ARCHIVE storage class.

    • Bulk - Bulk retrievals are Amazon S3 Glacier’s lowest-cost retrieval option, enabling you to retrieve large amounts, even petabytes, of data inexpensively in a day. Bulk retrievals typically complete within 5-12 hours from the GLACIER storage class and typically complete within 48 hours from the DEEP_ARCHIVE storage class.

    For more information about archive retrieval options and provisioned capacity for Expedited data access, see Restoring Archived Objects in the Amazon Simple Storage Service Developer Guide.

    You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed while it is in progress. You upgrade the speed of an in-progress restoration by issuing another restore request to the same object, setting a new Tier request element. When issuing a request to upgrade the restore tier, you must choose a tier that is faster than the tier that the in-progress restore is using. You must not change any other parameters, such as the Days request element. For more information, see Upgrading the Speed of an In-Progress Restore in the Amazon Simple Storage Service Developer Guide.

    To get the status of object restoration, you can send a HEAD request. Operations return the x-amz-restore header, which provides information about the restoration status, in the response. You can use Amazon S3 event notifications to notify you when a restore is initiated or completed. For more information, see Configuring Amazon S3 Event Notifications in the Amazon Simple Storage Service Developer Guide.

    After restoring an archived object, you can update the restoration period by reissuing the request with a new period. Amazon S3 updates the restoration period relative to the current time and charges only for the request-there are no data transfer charges. You cannot update the restoration period when Amazon S3 is actively processing your current restore request for the object.

    If your bucket has a lifecycle configuration with a rule that includes an expiration action, the object expiration overrides the life span that you specify in a restore request. For example, if you restore an object copy for 10 days, but the object is scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management in Amazon Simple Storage Service Developer Guide.

    Responses

    A successful operation returns either the 200 OK or 202 Accepted status code.

    • If the object copy is not previously restored, then Amazon S3 returns 202 Accepted in the response.

    • If the object copy is previously restored, Amazon S3 returns 200 OK in the response.

    Special Errors

      • Code: RestoreAlreadyInProgress

      • Cause: Object restore is already in progress. (This error does not apply to SELECT type requests.)

      • HTTP Status Code: 409 Conflict

      • SOAP Fault Code Prefix: Client

      • Code: GlacierExpeditedRetrievalNotAvailable

      • Cause: Glacier expedited retrievals are currently not available. Try again later. (Returned if there is insufficient capacity to process the Expedited request. This error applies only to Expedited retrievals and not to Standard or Bulk retrievals.)

      • HTTP Status Code: 503

      • SOAP Fault Code Prefix: N/A

    Related Resources

    ", "alias":"PostObjectRestore" }, "SelectObjectContent":{ @@ -950,7 +950,7 @@ "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, "output":{"shape":"SelectObjectContentOutput"}, - "documentation":"

    This operation filters the contents of an Amazon S3 object based on a simple Structured Query Language (SQL) statement. In the request, along with the SQL expression, you must also specify a data serialization format (JSON or CSV) of the object. Amazon S3 uses this to parse object data into records, and returns only records that match the specified SQL expression. You must also specify the data serialization format for the response.

    " + "documentation":"

    This operation filters the contents of an Amazon S3 object based on a simple structured query language (SQL) statement. In the request, along with the SQL expression, you must also specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses this format to parse object data into records, and returns only records that match the specified SQL expression. You must also specify the data serialization format for the response.

    For more information about Amazon S3 Select, see Selecting Content from Objects in the Amazon Simple Storage Service Developer Guide.

    For more information about using SQL with Amazon S3 Select, see SQL Reference for Amazon S3 Select and Glacier Select in the Amazon Simple Storage Service Developer Guide.

    Permissions

    You must have s3:GetObject permission for this operation. Amazon S3 Select does not support anonymous access. For more information about permissions, see Specifying Permissions in a Policy in the Amazon Simple Storage Service Developer Guide.

    Object Data Formats

    You can use Amazon S3 Select to query objects that have the following format properties:

    • CSV, JSON, and Parquet - Objects must be in CSV, JSON, or Parquet format.

    • UTF-8 - UTF-8 is the only encoding type Amazon S3 Select supports.

    • GZIP or BZIP2 - CSV and JSON files can be compressed using GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select supports for CSV and JSON files. Amazon S3 Select supports columnar compression for Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression for Parquet objects.

    • Server-side encryption - Amazon S3 Select supports querying objects that are protected with server-side encryption.

      For objects that are encrypted with customer-provided encryption keys (SSE-C), you must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side Encryption (Using Customer-Provided Encryption Keys) in the Amazon Simple Storage Service Developer Guide.

      For objects that are encrypted with Amazon S3 managed encryption keys (SSE-S3) and customer master keys (CMKs) stored in AWS Key Management Service (SSE-KMS), server-side encryption is handled transparently, so you don't need to specify anything. For more information about server-side encryption, including SSE-S3 and SSE-KMS, see Protecting Data Using Server-Side Encryption in the Amazon Simple Storage Service Developer Guide.

    Working with the Response Body

    Given the response size is unknown, Amazon S3 Select streams the response as a series of messages and includes a Transfer-Encoding header with chunked as its value in the response. For more information, see RESTSelectObjectAppendix .

    GetObject Support

    The SelectObjectContent operation does not support the following GetObject functionality. For more information, see GetObject.

    • Range: While you can specify a scan range for a Amazon S3 Select request, see SelectObjectContentRequest$ScanRange in the request parameters below, you cannot specify the range of bytes of an object to return.

    • GLACIER, DEEP_ARCHIVE and REDUCED_REDUNDANCY storage classes: You cannot specify the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes. For more information, about storage classes see Storage Classes in the Amazon Simple Storage Service Developer Guide.

    Special Errors

    For a list of special errors for this operation and for general information about Amazon S3 errors and a list of error codes, see ErrorResponses

    Related Resources

    " }, "UploadPart":{ "name":"UploadPart", @@ -961,7 +961,7 @@ "input":{"shape":"UploadPartRequest"}, "output":{"shape":"UploadPartOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadUploadPart.html", - "documentation":"

    Uploads a part in a multipart upload.

    Note: After you initiate multipart upload and upload one or more parts, you must either complete or abort multipart upload in order to stop getting charged for storage of the uploaded parts. Only after you either complete or abort multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts storage.

    " + "documentation":"

    Uploads a part in a multipart upload.

    In this operation, you provide part data in your request. However, you have an option to specify your existing Amazon S3 object as a data source for the part you are uploading. To upload a part from an existing object, you use the UploadPartCopy operation.

    You must initiate a multipart upload (see CreateMultipartUpload) before you can upload any part. In response to your initiate request, Amazon S3 returns an upload ID, a unique identifier, that you must include in your upload part request.

    Part numbers can be any number from 1 to 10,000, inclusive. A part number uniquely identifies a part and also defines its position within the object being created. If you upload a new part using the same part number that was used with a previous part, the previously uploaded part is overwritten. Each part must be at least 5 MB in size, except the last part. There is no size limit on the last part of your multipart upload.

    To ensure that data is not corrupted when traversing the network, specify the Content-MD5 header in the upload part request. Amazon S3 checks the part data against the provided MD5 value. If they do not match, Amazon S3 returns an error.

    Note: After you initiate multipart upload and upload one or more parts, you must either complete or abort multipart upload in order to stop getting charged for storage of the uploaded parts. Only after you either complete or abort multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts storage.

    For more information on multipart uploads, go to Multipart Upload Overview in the Amazon Simple Storage Service Developer Guide .

    For information on the permissions required to use the multipart upload API, go to Multipart Upload API and Permissions in the Amazon Simple Storage Service Developer Guide.

    You can optionally request server-side encryption where Amazon S3 encrypts your data as it writes it to disks in its data centers and decrypts it for you when you access it. You have the option of providing your own encryption key, or you can use the AWS managed encryption keys. If you choose to provide your own encryption key, the request headers you provide in the request must match the headers you used in the request to initiate the upload by using CreateMultipartUpload. For more information, go to Using Server-Side Encryption in the Amazon Simple Storage Service Developer Guide.

    Server-side encryption is supported by the S3 Multipart Upload actions. Unless you are using a customer-provided encryption key, you don't need to specify the encryption parameters in each UploadPart request. Instead, you only need to specify the server-side encryption parameters in the initial Initiate Multipart request. For more information, see CreateMultipartUpload.

    If you requested server-side encryption using a customer-provided encryption key in your initiate multipart upload request, you must provide identical encryption information in each part upload using the following headers.

    • x-amz-server-side​-encryption​-customer-algorithm

    • x-amz-server-side​-encryption​-customer-key

    • x-amz-server-side​-encryption​-customer-key-MD5

    Special Errors

      • Code: NoSuchUpload

      • Cause: The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

    Related Resources

    " }, "UploadPartCopy":{ "name":"UploadPartCopy", @@ -972,7 +972,7 @@ "input":{"shape":"UploadPartCopyRequest"}, "output":{"shape":"UploadPartCopyOutput"}, "documentationUrl":"http://docs.amazonwebservices.com/AmazonS3/latest/API/mpUploadUploadPartCopy.html", - "documentation":"

    Uploads a part by copying data from an existing object as data source.

    " + "documentation":"

    Uploads a part by copying data from an existing object as data source. You specify the data source by adding the request header x-amz-copy-source in your request and a byte range by adding the request header x-amz-copy-source-range in your request.

    The minimum allowable part size for a multipart upload is 5 MB. For more information about multipart upload limits, go to Quick Facts in the Amazon Simple Storage Service Developer Guide.

    Instead of using an existing object as part data, you might use the UploadPart operation and provide data in your request.

    You must initiate a multipart upload before you can upload any part. In response to your initiate request. Amazon S3 returns a unique identifier, the upload ID, that you must include in your upload part request.

    For more information about using the UploadPartCopy operation, see the following:

    • For conceptual information about multipart uploads, see Uploading Objects Using Multipart Upload in the Amazon Simple Storage Service Developer Guide.

    • For information about permissions required to use the multipart upload API, see Multipart Upload API and Permissions in the Amazon Simple Storage Service Developer Guide.

    • For information about copying objects using a single atomic operation vs. the multipart upload, see Operations on Objects in the Amazon Simple Storage Service Developer Guide.

    • For information about using server-side encryption with customer-provided encryption keys with the UploadPartCopy operation, see CopyObject and UploadPart.

    Note the following additional considerations about the request headers x-amz-copy-source-if-match, x-amz-copy-source-if-none-match, x-amz-copy-source-if-unmodified-since, and x-amz-copy-source-if-modified-since:

    • Consideration 1 - If both of the x-amz-copy-source-if-match and x-amz-copy-source-if-unmodified-since headers are present in the request as follows:

      x-amz-copy-source-if-match condition evaluates to true, and;

      x-amz-copy-source-if-unmodified-since condition evaluates to false;

      Amazon S3 returns 200 OK and copies the data.

    • Consideration 2 - If both of the x-amz-copy-source-if-none-match and x-amz-copy-source-if-modified-since headers are present in the request as follows:

      x-amz-copy-source-if-none-match condition evaluates to false, and;

      x-amz-copy-source-if-modified-since condition evaluates to true;

      Amazon S3 returns 412 Precondition Failed response code.

    Versioning

    If your bucket has versioning enabled, you could have multiple versions of the same object. By default, x-amz-copy-source identifies the current version of the object to copy. If the current version is a delete marker and you don't specify a versionId in the x-amz-copy-source, Amazon S3 returns a 404 error, because the object does not exist. If you specify versionId in the x-amz-copy-source and the versionId is a delete marker, Amazon S3 returns an HTTP 400 error, because you are not allowed to specify a delete marker as a version for the x-amz-copy-source.

    You can optionally specify a specific version of the source object to copy by adding the versionId subresource as shown in the following example:

    x-amz-copy-source: /bucket/object?versionId=version id

    Special Errors

      • Code: NoSuchUpload

      • Cause: The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.

      • HTTP Status Code: 404 Not Found

      • Code: InvalidRequest

      • Cause: The specified copy source is not supported as a byte-range copy source.

      • HTTP Status Code: 400 Bad Request

    Related Resources

    " } }, "shapes":{ @@ -1007,7 +1007,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    Name of the bucket to which the multipart upload was initiated.

    ", + "documentation":"

    The bucket name to which the upload was taking place.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -1124,7 +1124,7 @@ "documentation":"

    Contains data related to access patterns to be collected and made available to analyze the tradeoffs between different storage classes.

    " } }, - "documentation":"

    Specifies the configuration and any analyses for the analytics filter of an Amazon S3 bucket.

    For more information, see GET Bucket analytics in the Amazon Simple Storage Service API Reference.

    " + "documentation":"

    Specifies the configuration and any analyses for the analytics filter of an Amazon S3 bucket.

    " }, "AnalyticsConfigurationList":{ "type":"list", @@ -1158,7 +1158,7 @@ "documentation":"

    A conjunction (logical AND) of predicates, which is used in evaluating an analytics filter. The operator must have at least two predicates.

    " } }, - "documentation":"

    " + "documentation":"

    The filter used to describe a set of objects for analyses. A filter must have exactly one prefix, one tag, or one conjunction (AnalyticsAndOperator). If no filter is provided, all objects will be considered in any analysis.

    " }, "AnalyticsId":{"type":"string"}, "AnalyticsS3BucketDestination":{ @@ -1185,7 +1185,7 @@ "documentation":"

    The prefix to use when exporting data. The prefix is prepended to all results.

    " } }, - "documentation":"

    " + "documentation":"

    Contains information about where to publish the analytics results.

    " }, "AnalyticsS3ExportFileFormat":{ "type":"string", @@ -1204,7 +1204,7 @@ "documentation":"

    Date the bucket was created.

    " } }, - "documentation":"

    " + "documentation":"

    In terms of implementation, a Bucket is a resource. An Amazon S3 bucket name is globally unique, and the namespace is shared by all AWS accounts.

    " }, "BucketAccelerateStatus":{ "type":"string", @@ -1224,7 +1224,7 @@ "type":"structure", "members":{ }, - "documentation":"

    ", + "documentation":"

    The bucket you tried to create already exists, and you own it. Amazon S3 returns this error in all AWS Regions except in the North Virginia Region. For legacy compatibility, if you re-create an existing bucket that you already own in the North Virginia Region, Amazon S3 returns 200 OK and resets the bucket access control lists (ACLs).

    ", "exception":true }, "BucketCannedACL":{ @@ -1267,12 +1267,9 @@ "BucketLoggingStatus":{ "type":"structure", "members":{ - "LoggingEnabled":{ - "shape":"LoggingEnabled", - "documentation":"

    " - } + "LoggingEnabled":{"shape":"LoggingEnabled"} }, - "documentation":"

    " + "documentation":"

    Container for logging status information.

    " }, "BucketLogsPermission":{ "type":"string", @@ -1307,7 +1304,7 @@ "members":{ "CORSRules":{ "shape":"CORSRules", - "documentation":"

    A set of allowed origins and methods.

    ", + "documentation":"

    A set of origins and methods (cross-origin access that you want to allow). You can add up to 100 rules to the configuration.

    ", "locationName":"CORSRule" } }, @@ -1357,60 +1354,60 @@ "members":{ "FileHeaderInfo":{ "shape":"FileHeaderInfo", - "documentation":"

    Describes the first line of input. Valid values: None, Ignore, Use.

    " + "documentation":"

    Describes the first line of input. Valid values are:

    • NONE: First line is not a header.

    • IGNORE: First line is a header, but you can't use the header values to indicate the column in an expression. You can use column position (such as _1, _2, …) to indicate the column (SELECT s._1 FROM OBJECT s).

    • Use: First line is a header, and you can use the header value to identify a column in an expression (SELECT \"name\" FROM OBJECT).

    " }, "Comments":{ "shape":"Comments", - "documentation":"

    The single character used to indicate a row should be ignored when present at the start of a row.

    " + "documentation":"

    A single character used to indicate that a row should be ignored when the character is present at the start of that row. You can specify any character to indicate a comment line.

    " }, "QuoteEscapeCharacter":{ "shape":"QuoteEscapeCharacter", - "documentation":"

    The single character used for escaping the quote character inside an already escaped value.

    " + "documentation":"

    A single character used for escaping the quotation mark character inside an already escaped value. For example, the value \"\"\" a , b \"\"\" is parsed as \" a , b \".

    " }, "RecordDelimiter":{ "shape":"RecordDelimiter", - "documentation":"

    The value used to separate individual records.

    " + "documentation":"

    A single character used to separate individual records in the input. Instead of the default value, you can specify an arbitrary delimiter.

    " }, "FieldDelimiter":{ "shape":"FieldDelimiter", - "documentation":"

    The value used to separate individual fields in a record.

    " + "documentation":"

    A single character used to separate individual fields in a record. You can specify an arbitrary delimiter.

    " }, "QuoteCharacter":{ "shape":"QuoteCharacter", - "documentation":"

    Value used for escaping where the field delimiter is part of the value.

    " + "documentation":"

    A single character used for escaping when the field delimiter is part of the value. For example, if the value is a, b, Amazon S3 wraps this field value in quotation marks, as follows: \" a , b \".

    Type: String

    Default: \"

    Ancestors: CSV

    " }, "AllowQuotedRecordDelimiter":{ "shape":"AllowQuotedRecordDelimiter", "documentation":"

    Specifies that CSV field values may contain quoted record delimiters and such records should be allowed. Default value is FALSE. Setting this value to TRUE may lower performance.

    " } }, - "documentation":"

    Describes how a CSV-formatted input object is formatted.

    " + "documentation":"

    Describes how an uncompressed comma-separated values (CSV)-formatted input object is formatted.

    " }, "CSVOutput":{ "type":"structure", "members":{ "QuoteFields":{ "shape":"QuoteFields", - "documentation":"

    Indicates whether or not all output fields should be quoted.

    " + "documentation":"

    Indicates whether to use quotation marks around output fields.

    • ALWAYS: Always use quotation marks for output fields.

    • ASNEEDED: Use quotation marks for output fields when needed.

    " }, "QuoteEscapeCharacter":{ "shape":"QuoteEscapeCharacter", - "documentation":"

    Th single character used for escaping the quote character inside an already escaped value.

    " + "documentation":"

    The single character used for escaping the quote character inside an already escaped value.

    " }, "RecordDelimiter":{ "shape":"RecordDelimiter", - "documentation":"

    The value used to separate individual records.

    " + "documentation":"

    A single character used to separate individual records in the output. Instead of the default value, you can specify an arbitrary delimiter.

    " }, "FieldDelimiter":{ "shape":"FieldDelimiter", - "documentation":"

    The value used to separate individual fields in a record.

    " + "documentation":"

    The value used to separate individual fields in a record. You can specify an arbitrary delimiter.

    " }, "QuoteCharacter":{ "shape":"QuoteCharacter", - "documentation":"

    The value used for escaping where the field delimiter is part of the value.

    " + "documentation":"

    A single character used for escaping when the field delimiter is part of the value. For example, if the value is a, b, Amazon S3 wraps this field value in quotation marks, as follows: \" a , b \".

    " } }, - "documentation":"

    Describes how CSV-formatted results are formatted.

    " + "documentation":"

    Describes how uncompressed comma-separated values (CSV)-formatted results are formatted.

    " }, "CacheControl":{"type":"string"}, "CloudFunction":{"type":"string"}, @@ -1424,19 +1421,19 @@ }, "Events":{ "shape":"EventList", - "documentation":"

    ", + "documentation":"

    Bucket events for which to send notifications.

    ", "locationName":"Event" }, "CloudFunction":{ "shape":"CloudFunction", - "documentation":"

    " + "documentation":"

    Lambda cloud function ARN that Amazon S3 can invoke when it detects events of the specified type.

    " }, "InvocationRole":{ "shape":"CloudFunctionInvocationRole", - "documentation":"

    " + "documentation":"

    The role supporting the invocation of the Lambda function

    " } }, - "documentation":"

    " + "documentation":"

    Container for specifying the AWS Lambda notification configuration.

    " }, "CloudFunctionInvocationRole":{"type":"string"}, "Code":{"type":"string"}, @@ -1446,10 +1443,10 @@ "members":{ "Prefix":{ "shape":"Prefix", - "documentation":"

    " + "documentation":"

    Container for the specified common prefix.

    " } }, - "documentation":"

    " + "documentation":"

    Container for all (if there are any) keys between Prefix and the next occurrence of the string specified by a delimiter. CommonPrefixes lists keys that act like subdirectories in the directory specified by Prefix. For example, if the prefix is notes/ and the delimiter is a slash (/) as in notes/summer/july, the common prefix is notes/summer/.

    " }, "CommonPrefixList":{ "type":"list", @@ -1461,15 +1458,15 @@ "members":{ "Location":{ "shape":"Location", - "documentation":"

    " + "documentation":"

    The URI that identifies the newly created object.

    " }, "Bucket":{ "shape":"BucketName", - "documentation":"

    " + "documentation":"

    The name of the bucket that contains the newly created object.

    " }, "Key":{ "shape":"ObjectKey", - "documentation":"

    " + "documentation":"

    The object key of the newly created object.

    " }, "Expiration":{ "shape":"Expiration", @@ -1479,23 +1476,23 @@ }, "ETag":{ "shape":"ETag", - "documentation":"

    Entity tag of the object.

    " + "documentation":"

    Entity tag that identifies the newly created object's data. Objects with different object data will have different entity tags. The entity tag is an opaque string. The entity tag may or may not be an MD5 digest of the object data. If the entity tag is not an MD5 digest of the object data, it will contain one or more nonhexadecimal characters and/or will consist of less than 32 or more than 32 hexadecimal digits.

    " }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    If you specified server-side encryption either with an Amazon S3-managed encryption key or an AWS KMS customer master key (CMK) in your initiate multipart upload request, the response includes this header. It confirms the encryption algorithm that Amazon S3 used to encrypt the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    Version of the object.

    ", + "documentation":"

    Version ID of the newly created object, in case the bucket has versioning turned on.

    ", "location":"header", "locationName":"x-amz-version-id" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -1516,25 +1513,25 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Name of the bucket to which the multipart upload was initiated.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Object key for which the multipart upload was initiated.

    ", "location":"uri", "locationName":"Key" }, "MultipartUpload":{ "shape":"CompletedMultipartUpload", - "documentation":"

    ", + "documentation":"

    The container for the multipart upload request information.

    ", "locationName":"CompleteMultipartUpload", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, "UploadId":{ "shape":"MultipartUploadId", - "documentation":"

    ", + "documentation":"

    ID for the initiated multipart upload.

    ", "location":"querystring", "locationName":"uploadId" }, @@ -1551,11 +1548,11 @@ "members":{ "Parts":{ "shape":"CompletedPartList", - "documentation":"

    ", + "documentation":"

    Array of CompletedPart data types.

    ", "locationName":"Part" } }, - "documentation":"

    " + "documentation":"

    The container for the completed multipart upload details.

    " }, "CompletedPart":{ "type":"structure", @@ -1569,7 +1566,7 @@ "documentation":"

    Part number that identifies the part. This is a positive integer between 1 and 10,000.

    " } }, - "documentation":"

    " + "documentation":"

    Details of the parts that were uploaded.

    " }, "CompletedPartList":{ "type":"list", @@ -1593,10 +1590,10 @@ }, "KeyPrefixEquals":{ "shape":"KeyPrefixEquals", - "documentation":"

    The object key name prefix when the redirect is applied. For example, to redirect requests for ExamplePage.html, the key prefix will be ExamplePage.html. To redirect request for all pages with the prefix docs/, the key prefix will be /docs, which identifies all objects in the docs/ folder. Required when the parent element Condition is specified and sibling HttpErrorCodeReturnedEquals is not specified. If both conditions are specified, both must be true for the redirect to be applied.

    " + "documentation":"

    The object key name prefix when the redirect is applied. For example, to redirect requests for ExamplePage.html, the key prefix will be ExamplePage.html. To redirect request for all pages with the prefix docs/, the key prefix will be /docs, which identifies all objects in the docs/ folder. Required when the parent element Condition is specified and sibling HttpErrorCodeReturnedEquals is not specified. If both conditions are specified, both must be true for the redirect to be applied.

    " } }, - "documentation":"

    Specifies a condition that must be met for a redirect to apply.

    " + "documentation":"

    A container for describing a condition that must be met for the specified redirect to apply. For example, 1. If request is for pages in the /docs folder, redirect to the /documents folder. 2. If request results in HTTP error 4xx, redirect request to another host where you might process the error.

    " }, "ConfirmRemoveSelfBucketAccess":{"type":"boolean"}, "ContentDisposition":{"type":"string"}, @@ -1618,7 +1615,7 @@ "members":{ "CopyObjectResult":{ "shape":"CopyObjectResult", - "documentation":"

    " + "documentation":"

    Container for all response elements.

    " }, "Expiration":{ "shape":"Expiration", @@ -1628,7 +1625,7 @@ }, "CopySourceVersionId":{ "shape":"CopySourceVersionId", - "documentation":"

    ", + "documentation":"

    Version of the copied object in the destination bucket.

    ", "location":"header", "locationName":"x-amz-copy-source-version-id" }, @@ -1640,7 +1637,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -1652,13 +1649,13 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -1692,7 +1689,7 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the destination bucket.

    ", "location":"uri", "locationName":"Bucket" }, @@ -1788,7 +1785,7 @@ }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    The key of the destination object.

    ", "location":"uri", "locationName":"Key" }, @@ -1812,7 +1809,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -1830,25 +1827,25 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    Specifies the AWS KMS key ID to use for object encryption. All GET and PUT requests for an object protected by AWS KMS will fail if not made via SSL or using SigV4. Documentation on configuring any of the officially supported AWS SDKs and CLI can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version

    ", + "documentation":"

    Specifies the AWS KMS key ID to use for object encryption. All GET and PUT requests for an object protected by AWS KMS will fail if not made via SSL or using SigV4. For information about configuring using any of the officially supported AWS SDKs and AWS CLI, see Specifying the Signature Version in Request Authentication in the Amazon S3 Developer Guide.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -1860,7 +1857,7 @@ }, "CopySourceSSECustomerAlgorithm":{ "shape":"CopySourceSSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use when decrypting the source object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use when decrypting the source object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-copy-source-server-side-encryption-customer-algorithm" }, @@ -1872,7 +1869,7 @@ }, "CopySourceSSECustomerKeyMD5":{ "shape":"CopySourceSSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-copy-source-server-side-encryption-customer-key-MD5" }, @@ -1883,19 +1880,19 @@ }, "Tagging":{ "shape":"TaggingHeader", - "documentation":"

    The tag-set for the object destination object this value must be used in conjunction with the TaggingDirective. The tag-set must be encoded as URL Query parameters

    ", + "documentation":"

    The tag-set for the object destination object this value must be used in conjunction with the TaggingDirective. The tag-set must be encoded as URL Query parameters.

    ", "location":"header", "locationName":"x-amz-tagging" }, "ObjectLockMode":{ "shape":"ObjectLockMode", - "documentation":"

    The object lock mode that you want to apply to the copied object.

    ", + "documentation":"

    The Object Lock mode that you want to apply to the copied object.

    ", "location":"header", "locationName":"x-amz-object-lock-mode" }, "ObjectLockRetainUntilDate":{ "shape":"ObjectLockRetainUntilDate", - "documentation":"

    The date and time when you want the copied object's object lock to expire.

    ", + "documentation":"

    The date and time when you want the copied object's Object Lock to expire.

    ", "location":"header", "locationName":"x-amz-object-lock-retain-until-date" }, @@ -1912,14 +1909,14 @@ "members":{ "ETag":{ "shape":"ETag", - "documentation":"

    " + "documentation":"

    Returns the ETag of the new object. The ETag reflects only changes to the contents of an object, not its metadata. The source and destination ETag is identical for a successfully copied object.

    " }, "LastModified":{ "shape":"LastModified", - "documentation":"

    " + "documentation":"

    Returns the date that the object was last modified.

    " } }, - "documentation":"

    " + "documentation":"

    Container for all response elements.

    " }, "CopyPartResult":{ "type":"structure", @@ -1933,7 +1930,7 @@ "documentation":"

    Date and time at which the object was uploaded.

    " } }, - "documentation":"

    " + "documentation":"

    Container for all response elements.

    " }, "CopySource":{ "type":"string", @@ -1956,17 +1953,17 @@ "members":{ "LocationConstraint":{ "shape":"BucketLocationConstraint", - "documentation":"

    Specifies the region where the bucket will be created. If you don't specify a region, the bucket is created in US East (N. Virginia) Region (us-east-1).

    " + "documentation":"

    Specifies the Region where the bucket will be created. If you don't specify a Region, the bucket is created in the US East (N. Virginia) Region (us-east-1).

    " } }, - "documentation":"

    " + "documentation":"

    The configuration information for the bucket.

    " }, "CreateBucketOutput":{ "type":"structure", "members":{ "Location":{ "shape":"Location", - "documentation":"

    ", + "documentation":"

    Specifies the Region where the bucket will be created. If you are creating a bucket on the US East (N. Virginia) Region (us-east-1), you do not need to specify the location.

    ", "location":"header", "locationName":"Location" } @@ -1984,13 +1981,13 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket to create.

    ", "location":"uri", "locationName":"Bucket" }, "CreateBucketConfiguration":{ "shape":"CreateBucketConfiguration", - "documentation":"

    ", + "documentation":"

    The configuration information for the bucket.

    ", "locationName":"CreateBucketConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, @@ -2026,7 +2023,7 @@ }, "ObjectLockEnabledForBucket":{ "shape":"ObjectLockEnabledForBucket", - "documentation":"

    Specifies whether you want Amazon S3 object lock to be enabled for the new bucket.

    ", + "documentation":"

    Specifies whether you want S3 Object Lock to be enabled for the new bucket.

    ", "location":"header", "locationName":"x-amz-bucket-object-lock-enabled" } @@ -2038,19 +2035,19 @@ "members":{ "AbortDate":{ "shape":"AbortDate", - "documentation":"

    Date when multipart upload will become eligible for abort operation by lifecycle.

    ", + "documentation":"

    If the bucket has a lifecycle rule configured with an action to abort incomplete multipart uploads and the prefix in the lifecycle rule matches the object name in the request, the response includes this header. The header indicates when the initiated multipart upload becomes eligible for an abort operation. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

    The response also includes the x-amz-abort-rule-id header that provides the ID of the lifecycle configuration rule that defines this action.

    ", "location":"header", "locationName":"x-amz-abort-date" }, "AbortRuleId":{ "shape":"AbortRuleId", - "documentation":"

    Id of the lifecycle rule that makes a multipart upload eligible for abort operation.

    ", + "documentation":"

    This header is returned along with the x-amz-abort-date header. It identifies the applicable lifecycle configuration rule that defines the action to abort incomplete multipart uploads.

    ", "location":"header", "locationName":"x-amz-abort-rule-id" }, "Bucket":{ "shape":"BucketName", - "documentation":"

    Name of the bucket to which the multipart upload was initiated.

    ", + "documentation":"

    Name of the bucket to which the multipart upload was initiated.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "locationName":"Bucket" }, "Key":{ @@ -2063,7 +2060,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -2075,13 +2072,13 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -2113,7 +2110,7 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket to which to initiate the upload

    ", "location":"uri", "locationName":"Bucket" }, @@ -2179,7 +2176,7 @@ }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Object key for which the multipart upload is to be initiated.

    ", "location":"uri", "locationName":"Key" }, @@ -2191,7 +2188,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -2209,25 +2206,25 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    Specifies the AWS KMS key ID to use for object encryption. All GET and PUT requests for an object protected by AWS KMS will fail if not made via SSL or using SigV4. Documentation on configuring any of the officially supported AWS SDKs and CLI can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version

    ", + "documentation":"

    Specifies the ID of the symmetric customer managed AWS KMS CMK to use for object encryption. All GET and PUT requests for an object protected by AWS KMS will fail if not made via SSL or using SigV4. For information about configuring using any of the officially supported AWS SDKs and AWS CLI, see Specifying the Signature Version in Request Authentication in the Amazon S3 Developer Guide.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -2244,19 +2241,19 @@ }, "Tagging":{ "shape":"TaggingHeader", - "documentation":"

    The tag-set for the object. The tag-set must be encoded as URL Query parameters

    ", + "documentation":"

    The tag-set for the object. The tag-set must be encoded as URL Query parameters.

    ", "location":"header", "locationName":"x-amz-tagging" }, "ObjectLockMode":{ "shape":"ObjectLockMode", - "documentation":"

    Specifies the object lock mode that you want to apply to the uploaded object.

    ", + "documentation":"

    Specifies the Object Lock mode that you want to apply to the uploaded object.

    ", "location":"header", "locationName":"x-amz-object-lock-mode" }, "ObjectLockRetainUntilDate":{ "shape":"ObjectLockRetainUntilDate", - "documentation":"

    Specifies the date and time when you want the object lock to expire.

    ", + "documentation":"

    Specifies the date and time when you want the Object Lock to expire.

    ", "location":"header", "locationName":"x-amz-object-lock-retain-until-date" }, @@ -2280,7 +2277,7 @@ "members":{ "Mode":{ "shape":"ObjectLockRetentionMode", - "documentation":"

    The default object lock retention mode you want to apply to new objects placed in the specified bucket.

    " + "documentation":"

    The default Object Lock retention mode you want to apply to new objects placed in the specified bucket.

    " }, "Days":{ "shape":"Days", @@ -2291,7 +2288,7 @@ "documentation":"

    The number of years that you want to specify for the default retention period.

    " } }, - "documentation":"

    The container element for specifying the default object lock retention settings for new objects placed in the specified bucket.

    " + "documentation":"

    The container element for specifying the default Object Lock retention settings for new objects placed in the specified bucket.

    " }, "Delete":{ "type":"structure", @@ -2299,7 +2296,7 @@ "members":{ "Objects":{ "shape":"ObjectIdentifierList", - "documentation":"

    ", + "documentation":"

    The objects to delete.

    ", "locationName":"Object" }, "Quiet":{ @@ -2307,7 +2304,7 @@ "documentation":"

    Element to enable quiet mode for the request. When you add this element, you must set its value to true.

    " } }, - "documentation":"

    " + "documentation":"

    Container for the objects to delete.

    " }, "DeleteBucketAnalyticsConfigurationRequest":{ "type":"structure", @@ -2336,7 +2333,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Specifies the bucket whose cors configuration is being deleted.

    ", "location":"uri", "locationName":"Bucket" } @@ -2381,7 +2378,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name of the lifecycle to delete.

    ", "location":"uri", "locationName":"Bucket" } @@ -2414,7 +2411,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" } @@ -2426,7 +2423,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket name.

    It can take a while to propagate the deletion of a replication configuration to all Amazon S3 systems.

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" } @@ -2438,7 +2435,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Specifies the bucket being deleted.

    ", "location":"uri", "locationName":"Bucket" } @@ -2450,7 +2447,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket that has the tag set to be removed.

    ", "location":"uri", "locationName":"Bucket" } @@ -2462,7 +2459,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name for which you want to remove the website configuration.

    ", "location":"uri", "locationName":"Bucket" } @@ -2474,7 +2471,7 @@ "members":{ "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    The account that created the delete marker.>

    " }, "Key":{ "shape":"ObjectKey", @@ -2493,17 +2490,17 @@ "documentation":"

    Date and time the object was last modified.

    " } }, - "documentation":"

    " + "documentation":"

    Information about the delete marker.

    " }, "DeleteMarkerReplication":{ "type":"structure", "members":{ "Status":{ "shape":"DeleteMarkerReplicationStatus", - "documentation":"

    The status of the delete marker replication.

    In the current implementation, Amazon S3 doesn't replicate the delete markers. The status must be Disabled.

    " + "documentation":"

    Indicates whether to replicate delete markers.

    In the current implementation, Amazon S3 doesn't replicate the delete markers. The status must be Disabled.

    " } }, - "documentation":"

    Specifies whether Amazon S3 should replicate delete makers.

    " + "documentation":"

    Specifies whether Amazon S3 replicates the delete markers. If you specify a Filter, you must specify this element. However, in the latest version of replication configuration (when Filter is specified), Amazon S3 doesn't replicate delete markers. Therefore, the DeleteMarkerReplication element can contain only <Status>Disabled</Status>. For an example configuration, see Basic Rule Configuration.

    If you don't specify the Filter element, Amazon S3 assumes that the replication configuration is the earlier version, V1. In the earlier version, Amazon S3 handled replication of delete markers differently. For more information, see Backward Compatibility.

    " }, "DeleteMarkerReplicationStatus":{ "type":"string", @@ -2549,19 +2546,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name of the bucket containing the object.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Key name of the object to delete.

    ", "location":"uri", "locationName":"Key" }, "MFA":{ "shape":"MFA", - "documentation":"

    The concatenation of the authentication device's serial number, a space, and the value that is displayed on your authentication device.

    ", + "documentation":"

    The concatenation of the authentication device's serial number, a space, and the value that is displayed on your authentication device. Required to permanently delete a versioned object if versioning is configured with MFA delete enabled.

    ", "location":"header", "locationName":"x-amz-mfa" }, @@ -2578,7 +2575,7 @@ }, "BypassGovernanceRetention":{ "shape":"BypassGovernanceRetention", - "documentation":"

    Indicates whether Amazon S3 object lock should bypass governance-mode restrictions to process this operation.

    ", + "documentation":"

    Indicates whether S3 Object Lock should bypass Governance-mode restrictions to process this operation.

    ", "location":"header", "locationName":"x-amz-bypass-governance-retention" } @@ -2604,13 +2601,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name containing the objects from which to remove the tags.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Name of the tag.

    ", "location":"uri", "locationName":"Key" }, @@ -2627,7 +2624,7 @@ "members":{ "Deleted":{ "shape":"DeletedObjects", - "documentation":"

    " + "documentation":"

    Container element for a successful delete. It identifies the object that was successfully deleted.

    " }, "RequestCharged":{ "shape":"RequestCharged", @@ -2636,7 +2633,7 @@ }, "Errors":{ "shape":"Errors", - "documentation":"

    ", + "documentation":"

    Container for a failed delete operation that describes the object that Amazon S3 attempted to delete and the error it encountered.

    ", "locationName":"Error" } } @@ -2650,19 +2647,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name containing the objects to delete.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Delete":{ "shape":"Delete", - "documentation":"

    ", + "documentation":"

    Container for the request.

    ", "locationName":"Delete", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, "MFA":{ "shape":"MFA", - "documentation":"

    The concatenation of the authentication device's serial number, a space, and the value that is displayed on your authentication device.

    ", + "documentation":"

    The concatenation of the authentication device's serial number, a space, and the value that is displayed on your authentication device. Required to permanently delete a versioned object if versioning is configured with MFA delete enabled.

    ", "location":"header", "locationName":"x-amz-mfa" }, @@ -2673,7 +2670,7 @@ }, "BypassGovernanceRetention":{ "shape":"BypassGovernanceRetention", - "documentation":"

    Specifies whether you want to delete this object even if it has a Governance-type object lock in place. You must have sufficient permissions to perform this operation.

    ", + "documentation":"

    Specifies whether you want to delete this object even if it has a Governance-type Object Lock in place. You must have sufficient permissions to perform this operation.

    ", "location":"header", "locationName":"x-amz-bypass-governance-retention" } @@ -2697,22 +2694,22 @@ "members":{ "Key":{ "shape":"ObjectKey", - "documentation":"

    " + "documentation":"

    The name of the deleted object.

    " }, "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    " + "documentation":"

    The version ID of the deleted object.

    " }, "DeleteMarker":{ "shape":"DeleteMarker", - "documentation":"

    " + "documentation":"

    Specifies whether the versioned object that was permanently deleted was (true) or was not (false) a delete marker. In a simple DELETE, this header indicates whether (true) or not (false) a delete marker was created.

    " }, "DeleteMarkerVersionId":{ "shape":"DeleteMarkerVersionId", - "documentation":"

    " + "documentation":"

    The version ID of the delete marker created as a result of the DELETE operation. If you delete a specific object version, the value returned by this header is the version ID of the object version deleted.

    " } }, - "documentation":"

    " + "documentation":"

    Information about the deleted object.

    " }, "DeletedObjects":{ "type":"list", @@ -2727,11 +2724,11 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The Amazon Resource Name (ARN) of the bucket where you want Amazon S3 to store replicas of the object identified by the rule.

    A replication configuration can replicate objects to only one destination bucket. If there are multiple rules in your replication configuration, all rules must specify the same destination bucket.

    " + "documentation":"

    The Amazon Resource Name (ARN) of the bucket where you want Amazon S3 to store the results.

    " }, "Account":{ "shape":"AccountId", - "documentation":"

    Destination bucket owner account ID. In a cross-account scenario, if you direct Amazon S3 to change replica ownership to the AWS account that owns the destination bucket by specifying the AccessControlTranslation property, this is the account ID of the destination bucket owner. For more information, see Cross-Region Replication Additional Configuration: Change Replica Owner in the Amazon Simple Storage Service Developer Guide.

    " + "documentation":"

    Destination bucket owner account ID. In a cross-account scenario, if you direct Amazon S3 to change replica ownership to the AWS account that owns the destination bucket by specifying the AccessControlTranslation property, this is the account ID of the destination bucket owner. For more information, see Replication Additional Configuration: Changing the Replica Owner in the Amazon Simple Storage Service Developer Guide.

    " }, "StorageClass":{ "shape":"StorageClass", @@ -2744,9 +2741,17 @@ "EncryptionConfiguration":{ "shape":"EncryptionConfiguration", "documentation":"

    A container that provides information about encryption. If SourceSelectionCriteria is specified, you must specify this element.

    " + }, + "ReplicationTime":{ + "shape":"ReplicationTime", + "documentation":"

    A container specifying S3 Replication Time Control (S3 RTC), including whether S3 RTC is enabled and the time when all objects and operations on objects must be replicated. Must be specified together with a Metrics block.

    " + }, + "Metrics":{ + "shape":"Metrics", + "documentation":"

    A container specifying replication metrics-related settings enabling metrics and Amazon S3 events for S3 Replication Time Control (S3 RTC). Must be specified together with a ReplicationTime block.

    " } }, - "documentation":"

    Specifies information about where to publish analysis or configuration results for an Amazon S3 bucket.

    " + "documentation":"

    Specifies information about where to publish analysis or configuration results for an Amazon S3 bucket and S3 Replication Time Control (S3 RTC).

    " }, "DisplayName":{"type":"string"}, "ETag":{"type":"string"}, @@ -2763,25 +2768,25 @@ "members":{ "EncryptionType":{ "shape":"ServerSideEncryption", - "documentation":"

    The server-side encryption algorithm used when storing job results in Amazon S3 (e.g., AES256, aws:kms).

    " + "documentation":"

    The server-side encryption algorithm used when storing job results in Amazon S3 (for example, AES256, aws:kms).

    " }, "KMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If the encryption type is aws:kms, this optional value specifies the AWS KMS key ID to use for encryption of job results.

    " + "documentation":"

    If the encryption type is aws:kms, this optional value specifies the ID of the symmetric customer managed AWS KMS CMK to use for encryption of job results. Amazon S3 only supports symmetric CMKs. For more information, see Using Symmetric and Asymmetric Keys in the AWS Key Management Service Developer Guide.

    " }, "KMSContext":{ "shape":"KMSContext", - "documentation":"

    If the encryption type is aws:kms, this optional value can be used to specify the encryption context for the restore results.

    " + "documentation":"

    If the encryption type is aws:kms, this optional value can be used to specify the encryption context for the restore results.

    " } }, - "documentation":"

    Describes the server-side encryption that will be applied to the restore results.

    " + "documentation":"

    Contains the type of server-side encryption used.

    " }, "EncryptionConfiguration":{ "type":"structure", "members":{ "ReplicaKmsKeyID":{ "shape":"ReplicaKmsKeyID", - "documentation":"

    Specifies the AWS KMS Key ID (Key ARN or Alias ARN) for the destination bucket. Amazon S3 uses this key to encrypt replica objects.

    " + "documentation":"

    Specifies the ID (Key ARN or Alias ARN) of the customer managed customer master key (CMK) stored in AWS Key Management Service (KMS) for the destination bucket. Amazon S3 uses this key to encrypt replica objects. Amazon S3 only supports symmetric customer managed CMKs. For more information, see Using Symmetric and Asymmetric Keys in the AWS Key Management Service Developer Guide.

    " } }, "documentation":"

    Specifies encryption-related information for an Amazon S3 bucket that is a destination for replicated objects.

    " @@ -2791,7 +2796,7 @@ "type":"structure", "members":{ }, - "documentation":"

    ", + "documentation":"

    A message that indicates the request is complete and no more messages will be sent. You should not assume that the request is complete until the client receives an EndEvent.

    ", "event":true }, "Error":{ @@ -2799,22 +2804,22 @@ "members":{ "Key":{ "shape":"ObjectKey", - "documentation":"

    " + "documentation":"

    The error key.

    " }, "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    " + "documentation":"

    The version ID of the error.

    " }, "Code":{ "shape":"Code", - "documentation":"

    " + "documentation":"

    The error code is a string that uniquely identifies an error condition. It is meant to be read and understood by programs that detect and handle errors by type.

    Amazon S3 error codes

      • Code: AccessDenied

      • Description: Access Denied

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: AccountProblem

      • Description: There is a problem with your AWS account that prevents the operation from completing successfully. Contact AWS Support for further assistance.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: AllAccessDisabled

      • Description: All access to this Amazon S3 resource has been disabled. Contact AWS Support for further assistance.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: AmbiguousGrantByEmailAddress

      • Description: The email address you provided is associated with more than one account.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: AuthorizationHeaderMalformed

      • Description: The authorization header you provided is invalid.

      • HTTP Status Code: 400 Bad Request

      • HTTP Status Code: N/A

      • Code: BadDigest

      • Description: The Content-MD5 you specified did not match what we received.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: BucketAlreadyExists

      • Description: The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again.

      • HTTP Status Code: 409 Conflict

      • SOAP Fault Code Prefix: Client

      • Code: BucketAlreadyOwnedByYou

      • Description: The bucket you tried to create already exists, and you own it. Amazon S3 returns this error in all AWS Regions except in the North Virginia Region. For legacy compatibility, if you re-create an existing bucket that you already own in the North Virginia Region, Amazon S3 returns 200 OK and resets the bucket access control lists (ACLs).

      • Code: 409 Conflict (in all Regions except the North Virginia Region)

      • SOAP Fault Code Prefix: Client

      • Code: BucketNotEmpty

      • Description: The bucket you tried to delete is not empty.

      • HTTP Status Code: 409 Conflict

      • SOAP Fault Code Prefix: Client

      • Code: CredentialsNotSupported

      • Description: This request does not support credentials.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: CrossLocationLoggingProhibited

      • Description: Cross-location logging not allowed. Buckets in one geographic location cannot log information to a bucket in another location.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: EntityTooSmall

      • Description: Your proposed upload is smaller than the minimum allowed object size.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: EntityTooLarge

      • Description: Your proposed upload exceeds the maximum allowed object size.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: ExpiredToken

      • Description: The provided token has expired.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: IllegalVersioningConfigurationException

      • Description: Indicates that the versioning configuration specified in the request is invalid.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: IncompleteBody

      • Description: You did not provide the number of bytes specified by the Content-Length HTTP header

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: IncorrectNumberOfFilesInPostRequest

      • Description: POST requires exactly one file upload per request.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InlineDataTooLarge

      • Description: Inline data exceeds the maximum allowed size.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InternalError

      • Description: We encountered an internal error. Please try again.

      • HTTP Status Code: 500 Internal Server Error

      • SOAP Fault Code Prefix: Server

      • Code: InvalidAccessKeyId

      • Description: The AWS access key ID you provided does not exist in our records.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: InvalidAddressingHeader

      • Description: You must specify the Anonymous role.

      • HTTP Status Code: N/A

      • SOAP Fault Code Prefix: Client

      • Code: InvalidArgument

      • Description: Invalid Argument

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidBucketName

      • Description: The specified bucket is not valid.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidBucketState

      • Description: The request is not valid with the current state of the bucket.

      • HTTP Status Code: 409 Conflict

      • SOAP Fault Code Prefix: Client

      • Code: InvalidDigest

      • Description: The Content-MD5 you specified is not valid.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidEncryptionAlgorithmError

      • Description: The encryption request you specified is not valid. The valid value is AES256.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidLocationConstraint

      • Description: The specified location constraint is not valid. For more information about Regions, see How to Select a Region for Your Buckets.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidObjectState

      • Description: The operation is not valid for the current state of the object.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: InvalidPart

      • Description: One or more of the specified parts could not be found. The part might not have been uploaded, or the specified entity tag might not have matched the part's entity tag.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidPartOrder

      • Description: The list of parts was not in ascending order. Parts list must be specified in order by part number.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidPayer

      • Description: All access to this object has been disabled. Please contact AWS Support for further assistance.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: InvalidPolicyDocument

      • Description: The content of the form does not meet the conditions specified in the policy document.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidRange

      • Description: The requested range cannot be satisfied.

      • HTTP Status Code: 416 Requested Range Not Satisfiable

      • SOAP Fault Code Prefix: Client

      • Code: InvalidRequest

      • Description: Please use AWS4-HMAC-SHA256.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: SOAP requests must be made over an HTTPS connection.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Acceleration is not supported for buckets with non-DNS compliant names.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Acceleration is not supported for buckets with periods (.) in their names.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Accelerate endpoint only supports virtual style requests.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Accelerate is not configured on this bucket.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Accelerate is disabled on this bucket.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Acceleration is not supported on this bucket. Contact AWS Support for more information.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidRequest

      • Description: Amazon S3 Transfer Acceleration cannot be enabled on this bucket. Contact AWS Support for more information.

      • HTTP Status Code: 400 Bad Request

      • Code: N/A

      • Code: InvalidSecurity

      • Description: The provided security credentials are not valid.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: InvalidSOAPRequest

      • Description: The SOAP request body is invalid.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidStorageClass

      • Description: The storage class you specified is not valid.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidTargetBucketForLogging

      • Description: The target bucket for logging does not exist, is not owned by you, or does not have the appropriate grants for the log-delivery group.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidToken

      • Description: The provided token is malformed or otherwise invalid.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: InvalidURI

      • Description: Couldn't parse the specified URI.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: KeyTooLongError

      • Description: Your key is too long.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MalformedACLError

      • Description: The XML you provided was not well-formed or did not validate against our published schema.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MalformedPOSTRequest

      • Description: The body of your POST request is not well-formed multipart/form-data.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MalformedXML

      • Description: This happens when the user sends malformed XML (XML that doesn't conform to the published XSD) for the configuration. The error message is, \"The XML you provided was not well-formed or did not validate against our published schema.\"

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MaxMessageLengthExceeded

      • Description: Your request was too big.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MaxPostPreDataLengthExceededError

      • Description: Your POST request fields preceding the upload file were too large.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MetadataTooLarge

      • Description: Your metadata headers exceed the maximum allowed metadata size.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MethodNotAllowed

      • Description: The specified method is not allowed against this resource.

      • HTTP Status Code: 405 Method Not Allowed

      • SOAP Fault Code Prefix: Client

      • Code: MissingAttachment

      • Description: A SOAP attachment was expected, but none were found.

      • HTTP Status Code: N/A

      • SOAP Fault Code Prefix: Client

      • Code: MissingContentLength

      • Description: You must provide the Content-Length HTTP header.

      • HTTP Status Code: 411 Length Required

      • SOAP Fault Code Prefix: Client

      • Code: MissingRequestBodyError

      • Description: This happens when the user sends an empty XML document as a request. The error message is, \"Request body is empty.\"

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MissingSecurityElement

      • Description: The SOAP 1.1 request is missing a security element.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: MissingSecurityHeader

      • Description: Your request is missing a required header.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: NoLoggingStatusForKey

      • Description: There is no such thing as a logging status subresource for a key.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: NoSuchBucket

      • Description: The specified bucket does not exist.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

      • Code: NoSuchBucketPolicy

      • Description: The specified bucket does not have a bucket policy.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

      • Code: NoSuchKey

      • Description: The specified key does not exist.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

      • Code: NoSuchLifecycleConfiguration

      • Description: The lifecycle configuration does not exist.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

      • Code: NoSuchUpload

      • Description: The specified multipart upload does not exist. The upload ID might be invalid, or the multipart upload might have been aborted or completed.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

      • Code: NoSuchVersion

      • Description: Indicates that the version ID specified in the request does not match an existing version.

      • HTTP Status Code: 404 Not Found

      • SOAP Fault Code Prefix: Client

      • Code: NotImplemented

      • Description: A header you provided implies functionality that is not implemented.

      • HTTP Status Code: 501 Not Implemented

      • SOAP Fault Code Prefix: Server

      • Code: NotSignedUp

      • Description: Your account is not signed up for the Amazon S3 service. You must sign up before you can use Amazon S3. You can sign up at the following URL: https://aws.amazon.com/s3

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: OperationAborted

      • Description: A conflicting conditional operation is currently in progress against this resource. Try again.

      • HTTP Status Code: 409 Conflict

      • SOAP Fault Code Prefix: Client

      • Code: PermanentRedirect

      • Description: The bucket you are attempting to access must be addressed using the specified endpoint. Send all future requests to this endpoint.

      • HTTP Status Code: 301 Moved Permanently

      • SOAP Fault Code Prefix: Client

      • Code: PreconditionFailed

      • Description: At least one of the preconditions you specified did not hold.

      • HTTP Status Code: 412 Precondition Failed

      • SOAP Fault Code Prefix: Client

      • Code: Redirect

      • Description: Temporary redirect.

      • HTTP Status Code: 307 Moved Temporarily

      • SOAP Fault Code Prefix: Client

      • Code: RestoreAlreadyInProgress

      • Description: Object restore is already in progress.

      • HTTP Status Code: 409 Conflict

      • SOAP Fault Code Prefix: Client

      • Code: RequestIsNotMultiPartContent

      • Description: Bucket POST must be of the enclosure-type multipart/form-data.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: RequestTimeout

      • Description: Your socket connection to the server was not read from or written to within the timeout period.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: RequestTimeTooSkewed

      • Description: The difference between the request time and the server's time is too large.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: RequestTorrentOfBucketError

      • Description: Requesting the torrent file of a bucket is not permitted.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: SignatureDoesNotMatch

      • Description: The request signature we calculated does not match the signature you provided. Check your AWS secret access key and signing method. For more information, see REST Authentication and SOAP Authentication for details.

      • HTTP Status Code: 403 Forbidden

      • SOAP Fault Code Prefix: Client

      • Code: ServiceUnavailable

      • Description: Reduce your request rate.

      • HTTP Status Code: 503 Service Unavailable

      • SOAP Fault Code Prefix: Server

      • Code: SlowDown

      • Description: Reduce your request rate.

      • HTTP Status Code: 503 Slow Down

      • SOAP Fault Code Prefix: Server

      • Code: TemporaryRedirect

      • Description: You are being redirected to the bucket while DNS updates.

      • HTTP Status Code: 307 Moved Temporarily

      • SOAP Fault Code Prefix: Client

      • Code: TokenRefreshRequired

      • Description: The provided token must be refreshed.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: TooManyBuckets

      • Description: You have attempted to create more buckets than allowed.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: UnexpectedContent

      • Description: This request does not support content.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: UnresolvableGrantByEmailAddress

      • Description: The email address you provided does not match any account on record.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

      • Code: UserKeyMustBeSpecified

      • Description: The bucket POST must contain the specified field name. If it is specified, check the order of the fields.

      • HTTP Status Code: 400 Bad Request

      • SOAP Fault Code Prefix: Client

    " }, "Message":{ "shape":"Message", - "documentation":"

    " + "documentation":"

    The error message contains a generic description of the error condition in English. It is intended for a human audience. Simple programs display the message directly to the end user if they encounter an error condition they don't know how or don't care to handle. Sophisticated programs with more exhaustive error handling and proper internationalization are more likely to ignore the error message.

    " } }, - "documentation":"

    " + "documentation":"

    Container for all error elements.

    " }, "ErrorDocument":{ "type":"structure", @@ -2825,7 +2830,7 @@ "documentation":"

    The object key name to use when a 4XX class error occurs.

    " } }, - "documentation":"

    " + "documentation":"

    The error information.

    " }, "Errors":{ "type":"list", @@ -2845,8 +2850,14 @@ "s3:ObjectRemoved:*", "s3:ObjectRemoved:Delete", "s3:ObjectRemoved:DeleteMarkerCreated", + "s3:ObjectRestore:*", "s3:ObjectRestore:Post", - "s3:ObjectRestore:Completed" + "s3:ObjectRestore:Completed", + "s3:Replication:*", + "s3:Replication:OperationFailedReplication", + "s3:Replication:OperationNotTracked", + "s3:Replication:OperationMissedThreshold", + "s3:Replication:OperationReplicatedAfterThreshold" ] }, "EventList":{ @@ -2854,6 +2865,24 @@ "member":{"shape":"Event"}, "flattened":true }, + "ExistingObjectReplication":{ + "type":"structure", + "required":["Status"], + "members":{ + "Status":{ + "shape":"ExistingObjectReplicationStatus", + "documentation":"

    " + } + }, + "documentation":"

    Optional configuration to replicate existing source bucket objects. For more information, see Replicating Existing Objects in the Amazon S3 Developer Guide.

    " + }, + "ExistingObjectReplicationStatus":{ + "type":"string", + "enum":[ + "Enabled", + "Disabled" + ] + }, "Expiration":{"type":"string"}, "ExpirationStatus":{ "type":"string", @@ -2902,7 +2931,7 @@ "FilterRuleList":{ "type":"list", "member":{"shape":"FilterRule"}, - "documentation":"

    A list of containers for the key value pair that defines the criteria for the filter rule.

    ", + "documentation":"

    A list of containers for the key-value pair that defines the criteria for the filter rule.

    ", "flattened":true }, "FilterRuleName":{ @@ -2939,7 +2968,7 @@ "members":{ "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    Container for the bucket owner's display name and ID.

    " }, "Grants":{ "shape":"Grants", @@ -2954,7 +2983,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Specifies the S3 bucket whose ACL is being requested.

    ", "location":"uri", "locationName":"Bucket" } @@ -2996,7 +3025,7 @@ "members":{ "CORSRules":{ "shape":"CORSRules", - "documentation":"

    ", + "documentation":"

    A set of origins and methods (cross-origin access that you want to allow). You can add up to 100 rules to the configuration.

    ", "locationName":"CORSRule" } } @@ -3007,7 +3036,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name for which to get the cors configuration.

    ", "location":"uri", "locationName":"Bucket" } @@ -3016,10 +3045,7 @@ "GetBucketEncryptionOutput":{ "type":"structure", "members":{ - "ServerSideEncryptionConfiguration":{ - "shape":"ServerSideEncryptionConfiguration", - "documentation":"

    " - } + "ServerSideEncryptionConfiguration":{"shape":"ServerSideEncryptionConfiguration"} }, "payload":"ServerSideEncryptionConfiguration" }, @@ -3071,7 +3097,7 @@ "members":{ "Rules":{ "shape":"LifecycleRules", - "documentation":"

    ", + "documentation":"

    Container for a lifecycle rule.

    ", "locationName":"Rule" } } @@ -3082,7 +3108,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to get the lifecycle information.

    ", "location":"uri", "locationName":"Bucket" } @@ -3093,7 +3119,7 @@ "members":{ "Rules":{ "shape":"Rules", - "documentation":"

    ", + "documentation":"

    Container for a lifecycle rule.

    ", "locationName":"Rule" } } @@ -3104,7 +3130,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to get the lifecycle information.

    ", "location":"uri", "locationName":"Bucket" } @@ -3115,7 +3141,7 @@ "members":{ "LocationConstraint":{ "shape":"BucketLocationConstraint", - "documentation":"

    " + "documentation":"

    Specifies the Region where the bucket resides. For a list of all the Amazon S3 supported location constraints by Region, see Regions and Endpoints.

    " } } }, @@ -3125,7 +3151,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to get the location.

    ", "location":"uri", "locationName":"Bucket" } @@ -3134,10 +3160,7 @@ "GetBucketLoggingOutput":{ "type":"structure", "members":{ - "LoggingEnabled":{ - "shape":"LoggingEnabled", - "documentation":"

    " - } + "LoggingEnabled":{"shape":"LoggingEnabled"} } }, "GetBucketLoggingRequest":{ @@ -3146,7 +3169,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name for which to get the logging information.

    ", "location":"uri", "locationName":"Bucket" } @@ -3189,7 +3212,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    Name of the bucket to get the notification configuration for.

    ", + "documentation":"

    Name of the bucket for which to get the notification configuration

    ", "location":"uri", "locationName":"Bucket" } @@ -3211,7 +3234,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name for which to get the bucket policy.

    ", "location":"uri", "locationName":"Bucket" } @@ -3242,10 +3265,7 @@ "GetBucketReplicationOutput":{ "type":"structure", "members":{ - "ReplicationConfiguration":{ - "shape":"ReplicationConfiguration", - "documentation":"

    " - } + "ReplicationConfiguration":{"shape":"ReplicationConfiguration"} }, "payload":"ReplicationConfiguration" }, @@ -3255,7 +3275,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name for which to get the replication information.

    ", "location":"uri", "locationName":"Bucket" } @@ -3276,7 +3296,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to get the payment request configuration

    ", "location":"uri", "locationName":"Bucket" } @@ -3288,7 +3308,7 @@ "members":{ "TagSet":{ "shape":"TagSet", - "documentation":"

    " + "documentation":"

    Contains the tag set.

    " } } }, @@ -3298,7 +3318,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to get the tagging information.

    ", "location":"uri", "locationName":"Bucket" } @@ -3324,7 +3344,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to get the versioning information.

    ", "location":"uri", "locationName":"Bucket" } @@ -3335,19 +3355,19 @@ "members":{ "RedirectAllRequestsTo":{ "shape":"RedirectAllRequestsTo", - "documentation":"

    " + "documentation":"

    Specifies the redirect behavior of all requests to a website endpoint of an Amazon S3 bucket.

    " }, "IndexDocument":{ "shape":"IndexDocument", - "documentation":"

    " + "documentation":"

    The name of the index document for the website.

    " }, "ErrorDocument":{ "shape":"ErrorDocument", - "documentation":"

    " + "documentation":"

    The name of the error document for the website.

    " }, "RoutingRules":{ "shape":"RoutingRules", - "documentation":"

    " + "documentation":"

    Rules that define when a redirect is applied and the redirect behavior.

    " } } }, @@ -3357,7 +3377,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name for which to get the website configuration.

    ", "location":"uri", "locationName":"Bucket" } @@ -3368,7 +3388,7 @@ "members":{ "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    Container for the bucket owner's display name and ID.

    " }, "Grants":{ "shape":"Grants", @@ -3391,13 +3411,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name that contains the object for which to get the ACL information.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    The key of the object for which to get the ACL information.

    ", "location":"uri", "locationName":"Key" }, @@ -3433,7 +3453,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket containing the object whose Legal Hold status you want to retrieve.

    ", + "documentation":"

    The bucket name containing the object whose Legal Hold status you want to retrieve.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -3461,7 +3481,7 @@ "members":{ "ObjectLockConfiguration":{ "shape":"ObjectLockConfiguration", - "documentation":"

    The specified bucket's object lock configuration.

    " + "documentation":"

    The specified bucket's Object Lock configuration.

    " } }, "payload":"ObjectLockConfiguration" @@ -3472,7 +3492,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket whose object lock configuration you want to retrieve.

    ", + "documentation":"

    The bucket whose Object Lock configuration you want to retrieve.

    ", "location":"uri", "locationName":"Bucket" } @@ -3494,13 +3514,13 @@ }, "AcceptRanges":{ "shape":"AcceptRanges", - "documentation":"

    ", + "documentation":"

    Indicates that a range of bytes was specified.

    ", "location":"header", "locationName":"accept-ranges" }, "Expiration":{ "shape":"Expiration", - "documentation":"

    If the object expiration is configured (see PUT Bucket lifecycle), the response includes this header. It includes the expiry-date and rule-id key value pairs providing object expiration information. The value of the rule-id is URL encoded.

    ", + "documentation":"

    If the object expiration is configured (see PUT Bucket lifecycle), the response includes this header. It includes the expiry-date and rule-id key-value pairs providing object expiration information. The value of the rule-id is URL encoded.

    ", "location":"header", "locationName":"x-amz-expiration" }, @@ -3524,13 +3544,13 @@ }, "ETag":{ "shape":"ETag", - "documentation":"

    An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL

    ", + "documentation":"

    An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL.

    ", "location":"header", "locationName":"ETag" }, "MissingMeta":{ "shape":"MissingMeta", - "documentation":"

    This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.

    ", + "documentation":"

    This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.

    ", "location":"header", "locationName":"x-amz-missing-meta" }, @@ -3590,7 +3610,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -3608,19 +3628,19 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, "StorageClass":{ "shape":"StorageClass", - "documentation":"

    ", + "documentation":"

    Provides storage class information of the object. Amazon S3 returns this header for all objects except for Standard storage class objects.

    ", "location":"header", "locationName":"x-amz-storage-class" }, @@ -3631,7 +3651,7 @@ }, "ReplicationStatus":{ "shape":"ReplicationStatus", - "documentation":"

    ", + "documentation":"

    Amazon S3 can return this if your request involves a bucket that is either a source or destination in a replication rule.

    ", "location":"header", "locationName":"x-amz-replication-status" }, @@ -3649,19 +3669,19 @@ }, "ObjectLockMode":{ "shape":"ObjectLockMode", - "documentation":"

    The object lock mode currently in place for this object.

    ", + "documentation":"

    The Object Lock mode currently in place for this object.

    ", "location":"header", "locationName":"x-amz-object-lock-mode" }, "ObjectLockRetainUntilDate":{ "shape":"ObjectLockRetainUntilDate", - "documentation":"

    The date and time when this object's object lock will expire.

    ", + "documentation":"

    The date and time when this object's Object Lock will expire.

    ", "location":"header", "locationName":"x-amz-object-lock-retain-until-date" }, "ObjectLockLegalHoldStatus":{ "shape":"ObjectLockLegalHoldStatus", - "documentation":"

    Indicates whether this object has an active legal hold. This field is only returned if you have permission to view an object's legal hold status.

    ", + "documentation":"

    Indicates whether this object has an active legal hold. This field is only returned if you have permission to view an object's legal hold status.

    ", "location":"header", "locationName":"x-amz-object-lock-legal-hold" } @@ -3677,7 +3697,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name containing the object.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -3707,49 +3727,49 @@ }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Key of the object to get.

    ", "location":"uri", "locationName":"Key" }, "Range":{ "shape":"Range", - "documentation":"

    Downloads the specified range bytes of an object. For more information about the HTTP Range header, go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

    ", + "documentation":"

    Downloads the specified range bytes of an object. For more information about the HTTP Range header, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

    ", "location":"header", "locationName":"Range" }, "ResponseCacheControl":{ "shape":"ResponseCacheControl", - "documentation":"

    Sets the Cache-Control header of the response.

    ", + "documentation":"

    Sets the Cache-Control header of the response.

    ", "location":"querystring", "locationName":"response-cache-control" }, "ResponseContentDisposition":{ "shape":"ResponseContentDisposition", - "documentation":"

    Sets the Content-Disposition header of the response

    ", + "documentation":"

    Sets the Content-Disposition header of the response

    ", "location":"querystring", "locationName":"response-content-disposition" }, "ResponseContentEncoding":{ "shape":"ResponseContentEncoding", - "documentation":"

    Sets the Content-Encoding header of the response.

    ", + "documentation":"

    Sets the Content-Encoding header of the response.

    ", "location":"querystring", "locationName":"response-content-encoding" }, "ResponseContentLanguage":{ "shape":"ResponseContentLanguage", - "documentation":"

    Sets the Content-Language header of the response.

    ", + "documentation":"

    Sets the Content-Language header of the response.

    ", "location":"querystring", "locationName":"response-content-language" }, "ResponseContentType":{ "shape":"ResponseContentType", - "documentation":"

    Sets the Content-Type header of the response.

    ", + "documentation":"

    Sets the Content-Type header of the response.

    ", "location":"querystring", "locationName":"response-content-type" }, "ResponseExpires":{ "shape":"ResponseExpires", - "documentation":"

    Sets the Expires header of the response.

    ", + "documentation":"

    Sets the Expires header of the response.

    ", "location":"querystring", "locationName":"response-expires" }, @@ -3761,19 +3781,19 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, @@ -3809,7 +3829,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket containing the object whose retention settings you want to retrieve.

    ", + "documentation":"

    The bucket name containing the object whose retention settings you want to retrieve.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -3838,13 +3858,13 @@ "members":{ "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    ", + "documentation":"

    The versionId of the object for which you got the tagging information.

    ", "location":"header", "locationName":"x-amz-version-id" }, "TagSet":{ "shape":"TagSet", - "documentation":"

    " + "documentation":"

    Contains the tag set.

    " } } }, @@ -3857,19 +3877,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name containing the object for which to get the tagging information.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Object key for which to get the tagging information.

    ", "location":"uri", "locationName":"Key" }, "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    ", + "documentation":"

    The versionId of the object for which to get the tagging information.

    ", "location":"querystring", "locationName":"versionId" } @@ -3880,7 +3900,7 @@ "members":{ "Body":{ "shape":"Body", - "documentation":"

    ", + "documentation":"

    A Bencoded dictionary as defined by the BitTorrent specification

    ", "streaming":true }, "RequestCharged":{ @@ -3900,13 +3920,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket containing the object for which to get the torrent files.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    The object key for which to get the information.

    ", "location":"uri", "locationName":"Key" }, @@ -3948,21 +3968,21 @@ "documentation":"

    Glacier retrieval tier at which the restore will be processed.

    " } }, - "documentation":"

    " + "documentation":"

    Container for Glacier job parameters.

    " }, "Grant":{ "type":"structure", "members":{ "Grantee":{ "shape":"Grantee", - "documentation":"

    " + "documentation":"

    The person being granted permissions.

    " }, "Permission":{ "shape":"Permission", "documentation":"

    Specifies the permission given to the grantee.

    " } }, - "documentation":"

    " + "documentation":"

    Container for grant information.

    " }, "GrantFullControl":{"type":"string"}, "GrantRead":{"type":"string"}, @@ -3996,7 +4016,7 @@ "documentation":"

    URI of the grantee group.

    " } }, - "documentation":"

    ", + "documentation":"

    Container for the person being granted permissions.

    ", "xmlNamespace":{ "prefix":"xsi", "uri":"http://www.w3.org/2001/XMLSchema-instance" @@ -4015,7 +4035,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" } @@ -4032,19 +4052,19 @@ }, "AcceptRanges":{ "shape":"AcceptRanges", - "documentation":"

    ", + "documentation":"

    Indicates that a range of bytes was specified.

    ", "location":"header", "locationName":"accept-ranges" }, "Expiration":{ "shape":"Expiration", - "documentation":"

    If the object expiration is configured (see PUT Bucket lifecycle), the response includes this header. It includes the expiry-date and rule-id key value pairs providing object expiration information. The value of the rule-id is URL encoded.

    ", + "documentation":"

    If the object expiration is configured (see PUT Bucket lifecycle), the response includes this header. It includes the expiry-date and rule-id key-value pairs providing object expiration information. The value of the rule-id is URL encoded.

    ", "location":"header", "locationName":"x-amz-expiration" }, "Restore":{ "shape":"Restore", - "documentation":"

    Provides information about object restoration operation and expiration time of the restored object copy.

    ", + "documentation":"

    If the object is an archived object (an object whose storage class is GLACIER), the response includes this header if either the archive restoration is in progress (see RestoreObject or an archive copy is already restored.

    If an archive copy is already restored, the header value indicates when Amazon S3 is scheduled to delete the object copy. For example:

    x-amz-restore: ongoing-request=\"false\", expiry-date=\"Fri, 23 Dec 2012 00:00:00 GMT\"

    If the object restoration is in progress, the header returns the value ongoing-request=\"true\".

    For more information about archiving objects, see Transitioning Objects: General Considerations.

    ", "location":"header", "locationName":"x-amz-restore" }, @@ -4062,13 +4082,13 @@ }, "ETag":{ "shape":"ETag", - "documentation":"

    An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL

    ", + "documentation":"

    An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL.

    ", "location":"header", "locationName":"ETag" }, "MissingMeta":{ "shape":"MissingMeta", - "documentation":"

    This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.

    ", + "documentation":"

    This is set to the number of metadata entries not returned in x-amz-meta headers. This can happen if you create metadata using an API like SOAP that supports more flexible metadata than the REST API. For example, using SOAP, you can create metadata whose values are not legal HTTP headers.

    ", "location":"header", "locationName":"x-amz-missing-meta" }, @@ -4122,7 +4142,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    If the object is stored using server-side encryption either with an AWS KMS customer master key (CMK) or an Amazon S3-managed encryption key, the response includes this header with the value of the server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -4140,19 +4160,19 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, "StorageClass":{ "shape":"StorageClass", - "documentation":"

    ", + "documentation":"

    Provides storage class information of the object. Amazon S3 returns this header for all objects except for Standard storage class objects.

    For more information, see Storage Classes.

    ", "location":"header", "locationName":"x-amz-storage-class" }, @@ -4163,7 +4183,7 @@ }, "ReplicationStatus":{ "shape":"ReplicationStatus", - "documentation":"

    ", + "documentation":"

    Amazon S3 can return this header if your request involves a bucket that is either a source or destination in a replication rule.

    In replication, you have a source bucket on which you configure replication and destination bucket where Amazon S3 stores object replicas. When you request an object (GetObject) or object metadata (HeadObject) from these buckets, Amazon S3 will return the x-amz-replication-status header in the response as follows:

    • If requesting an object from the source bucket — Amazon S3 will return the x-amz-replication-status header if the object in your request is eligible for replication.

      For example, suppose that in your replication configuration, you specify object prefix TaxDocs requesting Amazon S3 to replicate objects with key prefix TaxDocs. Any objects you upload with this key name prefix, for example TaxDocs/document1.pdf, are eligible for replication. For any object request with this key name prefix, Amazon S3 will return the x-amz-replication-status header with value PENDING, COMPLETED or FAILED indicating object replication status.

    • If requesting an object from the destination bucket — Amazon S3 will return the x-amz-replication-status header with value REPLICA if the object in your request is a replica that Amazon S3 created.

    For more information, see Replication.

    ", "location":"header", "locationName":"x-amz-replication-status" }, @@ -4175,19 +4195,19 @@ }, "ObjectLockMode":{ "shape":"ObjectLockMode", - "documentation":"

    The object lock mode currently in place for this object.

    ", + "documentation":"

    The Object Lock mode, if any, that's in effect for this object. This header is only returned if the requester has the s3:GetObjectRetention permission. For more information about S3 Object Lock, see Object Lock.

    ", "location":"header", "locationName":"x-amz-object-lock-mode" }, "ObjectLockRetainUntilDate":{ "shape":"ObjectLockRetainUntilDate", - "documentation":"

    The date and time when this object's object lock expires.

    ", + "documentation":"

    The date and time when the Object Lock retention period expires. This header is only returned if the requester has the s3:GetObjectRetention permission.

    ", "location":"header", "locationName":"x-amz-object-lock-retain-until-date" }, "ObjectLockLegalHoldStatus":{ "shape":"ObjectLockLegalHoldStatus", - "documentation":"

    The Legal Hold status for the specified object.

    ", + "documentation":"

    Specifies whether a legal hold is in effect for this object. This header is only returned if the requester has the s3:GetObjectLegalHold permission. This header is not returned if the specified version of this object has never had a legal hold applied. For more information about S3 Object Lock, see Object Lock.

    ", "location":"header", "locationName":"x-amz-object-lock-legal-hold" } @@ -4202,7 +4222,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket containing the object.

    ", "location":"uri", "locationName":"Bucket" }, @@ -4232,13 +4252,13 @@ }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    The object key.

    ", "location":"uri", "locationName":"Key" }, "Range":{ "shape":"Range", - "documentation":"

    Downloads the specified range bytes of an object. For more information about the HTTP Range header, go to http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

    ", + "documentation":"

    Downloads the specified range bytes of an object. For more information about the HTTP Range header, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

    ", "location":"header", "locationName":"Range" }, @@ -4250,19 +4270,19 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, @@ -4293,10 +4313,10 @@ "members":{ "Suffix":{ "shape":"Suffix", - "documentation":"

    A suffix that is appended to a request that is for a directory on the website endpoint (e.g. if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character.

    " + "documentation":"

    A suffix that is appended to a request that is for a directory on the website endpoint (for example,if the suffix is index.html and you make a request to samplebucket/images/ the data that is returned will be for the object with the key name images/index.html) The suffix must not be empty and must not include a slash character.

    " } }, - "documentation":"

    " + "documentation":"

    Container for the Suffix element.

    " }, "Initiated":{"type":"timestamp"}, "Initiator":{ @@ -4311,7 +4331,7 @@ "documentation":"

    Name of the Principal.

    " } }, - "documentation":"

    " + "documentation":"

    Container element that identifies who initiated the multipart upload.

    " }, "InputSerialization":{ "type":"structure", @@ -4390,19 +4410,19 @@ "documentation":"

    Contains the bucket name, file format, bucket owner (optional), and prefix (optional) where inventory results are published.

    " } }, - "documentation":"

    " + "documentation":"

    Specifies the inventory configuration for an Amazon S3 bucket.

    " }, "InventoryEncryption":{ "type":"structure", "members":{ "SSES3":{ "shape":"SSES3", - "documentation":"

    Specifies the use of SSE-S3 to encrypt delivered Inventory reports.

    ", + "documentation":"

    Specifies the use of SSE-S3 to encrypt delivered inventory reports.

    ", "locationName":"SSE-S3" }, "SSEKMS":{ "shape":"SSEKMS", - "documentation":"

    Specifies the use of SSE-KMS to encrypt delivered Inventory reports.

    ", + "documentation":"

    Specifies the use of SSE-KMS to encrypt delivered inventory reports.

    ", "locationName":"SSE-KMS" } }, @@ -4417,7 +4437,7 @@ "documentation":"

    The prefix that an object must have to be included in the inventory results.

    " } }, - "documentation":"

    " + "documentation":"

    Specifies an inventory filter. The inventory only includes objects that meet the filter's criteria.

    " }, "InventoryFormat":{ "type":"string", @@ -4478,7 +4498,7 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    The Amazon resource name (ARN) of the bucket where inventory results will be published.

    " + "documentation":"

    The Amazon Resource Name (ARN) of the bucket where inventory results will be published.

    " }, "Format":{ "shape":"InventoryFormat", @@ -4493,7 +4513,7 @@ "documentation":"

    Contains the type of server-side encryption used to encrypt the inventory results.

    " } }, - "documentation":"

    " + "documentation":"

    Contains the bucket name, file format, bucket owner (optional), and prefix (optional) where inventory results are published.

    " }, "InventorySchedule":{ "type":"structure", @@ -4504,7 +4524,7 @@ "documentation":"

    Specifies how frequently inventory results are produced.

    " } }, - "documentation":"

    " + "documentation":"

    Specifies the schedule for generating inventory results.

    " }, "IsEnabled":{"type":"boolean"}, "IsLatest":{"type":"boolean"}, @@ -4518,7 +4538,7 @@ "documentation":"

    The type of JSON. Valid values: Document, Lines.

    " } }, - "documentation":"

    " + "documentation":"

    Specifies JSON as object's input serialization format.

    " }, "JSONOutput":{ "type":"structure", @@ -4528,7 +4548,7 @@ "documentation":"

    The value used to separate individual records in the output.

    " } }, - "documentation":"

    " + "documentation":"

    Specifies JSON as request's output serialization format.

    " }, "JSONType":{ "type":"string", @@ -4560,10 +4580,7 @@ "documentation":"

    The Amazon S3 bucket event for which to invoke the AWS Lambda function. For more information, see Supported Event Types in the Amazon Simple Storage Service Developer Guide.

    ", "locationName":"Event" }, - "Filter":{ - "shape":"NotificationConfigurationFilter", - "documentation":"

    " - } + "Filter":{"shape":"NotificationConfigurationFilter"} }, "documentation":"

    A container for specifying the configuration for AWS Lambda notifications.

    " }, @@ -4579,11 +4596,11 @@ "members":{ "Rules":{ "shape":"Rules", - "documentation":"

    ", + "documentation":"

    Specifies lifecycle configuration rules for an Amazon S3 bucket.

    ", "locationName":"Rule" } }, - "documentation":"

    " + "documentation":"

    Container for lifecycle rules. You can add as many as 1000 rules.

    " }, "LifecycleExpiration":{ "type":"structure", @@ -4601,7 +4618,7 @@ "documentation":"

    Indicates whether Amazon S3 will remove a delete marker with no noncurrent versions. If set to true, the delete marker will be expired; if set to false the policy takes no action. This cannot be specified with Days or Date in a Lifecycle Expiration Policy.

    " } }, - "documentation":"

    " + "documentation":"

    Container for the expiration for the lifecycle of the object.

    " }, "LifecycleRule":{ "type":"structure", @@ -4609,7 +4626,7 @@ "members":{ "Expiration":{ "shape":"LifecycleExpiration", - "documentation":"

    " + "documentation":"

    Specifies the expiration for the lifecycle of the object in the form of date, days and, whether the object has a delete marker.

    " }, "ID":{ "shape":"ID", @@ -4617,44 +4634,35 @@ }, "Prefix":{ "shape":"Prefix", - "documentation":"

    Prefix identifying one or more objects to which the rule applies. This is No longer used; use Filter instead.

    ", + "documentation":"

    Prefix identifying one or more objects to which the rule applies. This is No longer used; use Filter instead.

    ", "deprecated":true }, - "Filter":{ - "shape":"LifecycleRuleFilter", - "documentation":"

    " - }, + "Filter":{"shape":"LifecycleRuleFilter"}, "Status":{ "shape":"ExpirationStatus", "documentation":"

    If 'Enabled', the rule is currently being applied. If 'Disabled', the rule is not currently being applied.

    " }, "Transitions":{ "shape":"TransitionList", - "documentation":"

    ", + "documentation":"

    Specifies when an Amazon S3 object transitions to a specified storage class.

    ", "locationName":"Transition" }, "NoncurrentVersionTransitions":{ "shape":"NoncurrentVersionTransitionList", - "documentation":"

    ", + "documentation":"

    Specifies the transition rule for the lifecycle rule that describes when noncurrent objects transition to a specific storage class. If your bucket is versioning-enabled (or versioning is suspended), you can set this action to request that Amazon S3 transition noncurrent object versions to a specific storage class at a set period in the object's lifetime.

    ", "locationName":"NoncurrentVersionTransition" }, - "NoncurrentVersionExpiration":{ - "shape":"NoncurrentVersionExpiration", - "documentation":"

    " - }, - "AbortIncompleteMultipartUpload":{ - "shape":"AbortIncompleteMultipartUpload", - "documentation":"

    " - } + "NoncurrentVersionExpiration":{"shape":"NoncurrentVersionExpiration"}, + "AbortIncompleteMultipartUpload":{"shape":"AbortIncompleteMultipartUpload"} }, - "documentation":"

    " + "documentation":"

    A lifecycle rule for individual objects in an Amazon S3 bucket.

    " }, "LifecycleRuleAndOperator":{ "type":"structure", "members":{ "Prefix":{ "shape":"Prefix", - "documentation":"

    " + "documentation":"

    Prefix identifying one or more objects to which the rule applies.

    " }, "Tags":{ "shape":"TagSet", @@ -4676,12 +4684,9 @@ "shape":"Tag", "documentation":"

    This tag must exist in the object's tag set in order for the rule to apply.

    " }, - "And":{ - "shape":"LifecycleRuleAndOperator", - "documentation":"

    " - } + "And":{"shape":"LifecycleRuleAndOperator"} }, - "documentation":"

    The Filter is used to identify objects that a Lifecycle Rule applies to. A Filter must have exactly one of Prefix, Tag, or And specified.

    " + "documentation":"

    The Filter is used to identify objects that a Lifecycle Rule applies to. A Filter must have exactly one of Prefix, Tag, or And specified.

    " }, "LifecycleRules":{ "type":"list", @@ -4697,11 +4702,11 @@ }, "ContinuationToken":{ "shape":"Token", - "documentation":"

    The ContinuationToken that represents where this request began.

    " + "documentation":"

    The marker that is used as a starting point for this analytics configuration list response. This value is present if it was sent in the request.

    " }, "NextContinuationToken":{ "shape":"NextToken", - "documentation":"

    NextContinuationToken is sent when isTruncated is true, which indicates that there are more analytics configurations to list. The next request must include this NextContinuationToken. The token is obfuscated and is not a usable value.

    " + "documentation":"

    NextContinuationToken is sent when isTruncated is true, which indicates that there are more analytics configurations to list. The next request must include this NextContinuationToken. The token is obfuscated and is not a usable value.

    " }, "AnalyticsConfigurationList":{ "shape":"AnalyticsConfigurationList", @@ -4742,11 +4747,11 @@ }, "IsTruncated":{ "shape":"IsTruncated", - "documentation":"

    Indicates whether the returned list of inventory configurations is truncated in this response. A value of true indicates that the list is truncated.

    " + "documentation":"

    Tells whether the returned list of inventory configurations is complete. A value of true indicates that the list is not complete and the NextContinuationToken is provided for a subsequent request.

    " }, "NextContinuationToken":{ "shape":"NextToken", - "documentation":"

    The marker used to continue this inventory configuration listing. Use the NextContinuationToken from this response to continue the listing in a subsequent request. The continuation token is an opaque value that Amazon S3 understands.

    " + "documentation":"

    The marker used to continue this inventory configuration listing. Use the NextContinuationToken from this response to continue the listing in a subsequent request. The continuation token is an opaque value that Amazon S3 understands.

    " } } }, @@ -4781,7 +4786,7 @@ }, "NextContinuationToken":{ "shape":"NextToken", - "documentation":"

    The marker used to continue a metrics configuration listing that has been truncated. Use the NextContinuationToken from a previously truncated list response to continue the listing. The continuation token is an opaque value that Amazon S3 understands.

    " + "documentation":"

    The marker used to continue a metrics configuration listing that has been truncated. Use the NextContinuationToken from a previously truncated list response to continue the listing. The continuation token is an opaque value that Amazon S3 understands.

    " }, "MetricsConfigurationList":{ "shape":"MetricsConfigurationList", @@ -4813,11 +4818,11 @@ "members":{ "Buckets":{ "shape":"Buckets", - "documentation":"

    " + "documentation":"

    The list of buckets owned by the requestor.

    " }, "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    The owner of the buckets listed.

    " } } }, @@ -4846,11 +4851,11 @@ }, "Delimiter":{ "shape":"Delimiter", - "documentation":"

    " + "documentation":"

    Contains the delimiter you specified in the request. If you don't specify a delimiter in your request, this element is absent from the response.

    " }, "NextUploadIdMarker":{ "shape":"NextUploadIdMarker", - "documentation":"

    When a list is truncated, this element specifies the value that should be used for the upload-id-marker request parameter in a subsequent request.

    " + "documentation":"

    When a list is truncated, this element specifies the value that should be used for the upload-id-marker request parameter in a subsequent request.

    " }, "MaxUploads":{ "shape":"MaxUploads", @@ -4862,16 +4867,16 @@ }, "Uploads":{ "shape":"MultipartUploadList", - "documentation":"

    ", + "documentation":"

    Container for elements related to a particular multipart upload. A response can contain zero or more Upload elements.

    ", "locationName":"Upload" }, "CommonPrefixes":{ "shape":"CommonPrefixList", - "documentation":"

    " + "documentation":"

    If you specify a delimiter in the request, then the result returns each distinct key prefix containing the delimiter in a CommonPrefixes element. The distinct key prefixes are returned in the Prefix child element.

    " }, "EncodingType":{ "shape":"EncodingType", - "documentation":"

    Encoding type used by Amazon S3 to encode object keys in the response.

    " + "documentation":"

    Encoding type used by Amazon S3 to encode object keys in the response.

    If you specify encoding-type request parameter, Amazon S3 includes this element in the response, and returns encoded key name values in the following response elements:

    Delimiter, KeyMarker, Prefix, NextKeyMarker, Key.

    " } } }, @@ -4881,13 +4886,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Name of the bucket to which the multipart upload was initiated.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Delimiter":{ "shape":"Delimiter", - "documentation":"

    Character you use to group keys.

    ", + "documentation":"

    Character you use to group keys.

    All keys that contain the same string between the prefix, if specified, and the first occurrence of the delimiter after the prefix are grouped under a single result element, CommonPrefixes. If you don't specify the prefix parameter, then the substring starts at the beginning of the key. The keys that are grouped under CommonPrefixes result element are not returned elsewhere in the response.

    ", "location":"querystring", "locationName":"delimiter" }, @@ -4898,7 +4903,7 @@ }, "KeyMarker":{ "shape":"KeyMarker", - "documentation":"

    Together with upload-id-marker, this parameter specifies the multipart upload after which listing should begin.

    ", + "documentation":"

    Together with upload-id-marker, this parameter specifies the multipart upload after which listing should begin.

    If upload-id-marker is not specified, only the keys lexicographically greater than the specified key-marker will be included in the list.

    If upload-id-marker is specified, any multipart uploads for a key equal to the key-marker might also be included, provided those multipart uploads have upload IDs lexicographically greater than the specified upload-id-marker.

    ", "location":"querystring", "locationName":"key-marker" }, @@ -4910,13 +4915,13 @@ }, "Prefix":{ "shape":"Prefix", - "documentation":"

    Lists in-progress uploads only for those keys that begin with the specified prefix.

    ", + "documentation":"

    Lists in-progress uploads only for those keys that begin with the specified prefix. You can use prefixes to separate a bucket into different grouping of keys. (You can think of using prefix to make groups in the same way you'd use a folder in a file system.)

    ", "location":"querystring", "locationName":"prefix" }, "UploadIdMarker":{ "shape":"UploadIdMarker", - "documentation":"

    Together with key-marker, specifies the multipart upload after which listing should begin. If key-marker is not specified, the upload-id-marker parameter is ignored.

    ", + "documentation":"

    Together with key-marker, specifies the multipart upload after which listing should begin. If key-marker is not specified, the upload-id-marker parameter is ignored. Otherwise, any multipart uploads for a key equal to the key-marker might be included in the list only if they have an upload ID lexicographically greater than the specified upload-id-marker.

    ", "location":"querystring", "locationName":"upload-id-marker" } @@ -4927,57 +4932,57 @@ "members":{ "IsTruncated":{ "shape":"IsTruncated", - "documentation":"

    A flag that indicates whether or not Amazon S3 returned all of the results that satisfied the search criteria. If your results were truncated, you can make a follow-up paginated request using the NextKeyMarker and NextVersionIdMarker response parameters as a starting place in another request to return the rest of the results.

    " + "documentation":"

    A flag that indicates whether Amazon S3 returned all of the results that satisfied the search criteria. If your results were truncated, you can make a follow-up paginated request using the NextKeyMarker and NextVersionIdMarker response parameters as a starting place in another request to return the rest of the results.

    " }, "KeyMarker":{ "shape":"KeyMarker", - "documentation":"

    Marks the last Key returned in a truncated response.

    " + "documentation":"

    Marks the last key returned in a truncated response.

    " }, "VersionIdMarker":{ "shape":"VersionIdMarker", - "documentation":"

    " + "documentation":"

    Marks the last version of the key returned in a truncated response.

    " }, "NextKeyMarker":{ "shape":"NextKeyMarker", - "documentation":"

    Use this value for the key marker request parameter in a subsequent request.

    " + "documentation":"

    When the number of responses exceeds the value of MaxKeys, NextKeyMarker specifies the first key not returned that satisfies the search criteria. Use this value for the key-marker request parameter in a subsequent request.

    " }, "NextVersionIdMarker":{ "shape":"NextVersionIdMarker", - "documentation":"

    Use this value for the next version id marker parameter in a subsequent request.

    " + "documentation":"

    When the number of responses exceeds the value of MaxKeys, NextVersionIdMarker specifies the first object version not returned that satisfies the search criteria. Use this value for the version-id-marker request parameter in a subsequent request.

    " }, "Versions":{ "shape":"ObjectVersionList", - "documentation":"

    ", + "documentation":"

    Container for version information.

    ", "locationName":"Version" }, "DeleteMarkers":{ "shape":"DeleteMarkers", - "documentation":"

    ", + "documentation":"

    Container for an object that is a delete marker.

    ", "locationName":"DeleteMarker" }, "Name":{ "shape":"BucketName", - "documentation":"

    " + "documentation":"

    Bucket name.

    " }, "Prefix":{ "shape":"Prefix", - "documentation":"

    " + "documentation":"

    Selects objects that start with the value supplied by this parameter.

    " }, "Delimiter":{ "shape":"Delimiter", - "documentation":"

    " + "documentation":"

    The delimiter grouping the included keys. A delimiter is a character that you specify to group keys. All keys that contain the same string between the prefix and the first occurrence of the delimiter are grouped under a single result element in CommonPrefixes. These groups are counted as one result against the max-keys limitation. These keys are not returned elsewhere in the response.

    " }, "MaxKeys":{ "shape":"MaxKeys", - "documentation":"

    " + "documentation":"

    Specifies the maximum number of objects to return.

    " }, "CommonPrefixes":{ "shape":"CommonPrefixList", - "documentation":"

    " + "documentation":"

    All of the keys rolled up into a common prefix count as a single return when calculating the number of returns.

    " }, "EncodingType":{ "shape":"EncodingType", - "documentation":"

    Encoding type used by Amazon S3 to encode object keys in the response.

    " + "documentation":"

    Encoding type used by Amazon S3 to encode object key names in the XML response.

    If you specify encoding-type request parameter, Amazon S3 includes this element in the response, and returns encoded key name values in the following response elements:

    KeyMarker, NextKeyMarker, Prefix, Key, and Delimiter.

    " } } }, @@ -4987,13 +4992,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name that contains the objects.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Delimiter":{ "shape":"Delimiter", - "documentation":"

    A delimiter is a character you use to group keys.

    ", + "documentation":"

    A delimiter is a character that you specify to group keys. All keys that contain the same string between the prefix and the first occurrence of the delimiter are grouped under a single result element in CommonPrefixes. These groups are counted as one result against the max-keys limitation. These keys are not returned elsewhere in the response.

    ", "location":"querystring", "locationName":"delimiter" }, @@ -5010,13 +5015,13 @@ }, "MaxKeys":{ "shape":"MaxKeys", - "documentation":"

    Sets the maximum number of keys returned in the response. The response might contain fewer keys but will never contain more.

    ", + "documentation":"

    Sets the maximum number of keys returned in the response. The response might contain fewer keys but will never contain more. If additional keys satisfy the search criteria, but were not returned because max-keys was exceeded, the response contains <isTruncated>true</isTruncated>. To return the additional keys, see key-marker and version-id-marker.

    ", "location":"querystring", "locationName":"max-keys" }, "Prefix":{ "shape":"Prefix", - "documentation":"

    Limits the response to keys that begin with the specified prefix.

    ", + "documentation":"

    Use this parameter to select only those keys that begin with the specified prefix. You can use prefixes to separate a bucket into different groupings of keys. (You can think of using prefix to make groups in the same way you'd use a folder in a file system.) You can use prefix with delimiter to roll up numerous objects into a single result under CommonPrefixes.

    ", "location":"querystring", "locationName":"prefix" }, @@ -5033,11 +5038,11 @@ "members":{ "IsTruncated":{ "shape":"IsTruncated", - "documentation":"

    A flag that indicates whether or not Amazon S3 returned all of the results that satisfied the search criteria.

    " + "documentation":"

    A flag that indicates whether Amazon S3 returned all of the results that satisfied the search criteria.

    " }, "Marker":{ "shape":"Marker", - "documentation":"

    " + "documentation":"

    Indicates where in the bucket listing begins. Marker is included in the response if it was sent with the request.

    " }, "NextMarker":{ "shape":"NextMarker", @@ -5045,27 +5050,27 @@ }, "Contents":{ "shape":"ObjectList", - "documentation":"

    " + "documentation":"

    Metadata about each object returned.

    " }, "Name":{ "shape":"BucketName", - "documentation":"

    " + "documentation":"

    Bucket name.

    " }, "Prefix":{ "shape":"Prefix", - "documentation":"

    " + "documentation":"

    Keys that begin with the indicated prefix.

    " }, "Delimiter":{ "shape":"Delimiter", - "documentation":"

    " + "documentation":"

    Causes keys that contain the same string between the prefix and the first occurrence of the delimiter to be rolled up into a single result element in the CommonPrefixes collection. These rolled-up keys are not returned elsewhere in the response. Each rolled-up result counts as only one return against the MaxKeys value.

    " }, "MaxKeys":{ "shape":"MaxKeys", - "documentation":"

    " + "documentation":"

    The maximum number of keys returned in the response body.

    " }, "CommonPrefixes":{ "shape":"CommonPrefixList", - "documentation":"

    " + "documentation":"

    All of the keys rolled up in a common prefix count as a single return when calculating the number of returns.

    A response can contain CommonPrefixes only if you specify a delimiter.

    CommonPrefixes contains all (if there are any) keys between Prefix and the next occurrence of the string specified by the delimiter.

    CommonPrefixes lists keys that act like subdirectories in the directory specified by Prefix.

    For example, if the prefix is notes/ and the delimiter is a slash (/) as in notes/summer/july, the common prefix is notes/summer/. All of the keys that roll up into a common prefix count as a single return when calculating the number of returns.

    " }, "EncodingType":{ "shape":"EncodingType", @@ -5079,7 +5084,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket containing the objects.

    ", "location":"uri", "locationName":"Bucket" }, @@ -5125,7 +5130,7 @@ "members":{ "IsTruncated":{ "shape":"IsTruncated", - "documentation":"

    A flag that indicates whether or not Amazon S3 returned all of the results that satisfied the search criteria.

    " + "documentation":"

    Set to false if all of the results were returned. Set to true if more keys are available to return. If the number of results exceeds that specified by MaxKeys, all of the results might not be returned.

    " }, "Contents":{ "shape":"ObjectList", @@ -5133,15 +5138,15 @@ }, "Name":{ "shape":"BucketName", - "documentation":"

    Name of the bucket to list.

    " + "documentation":"

    Bucket name.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    " }, "Prefix":{ "shape":"Prefix", - "documentation":"

    Limits the response to keys that begin with the specified prefix.

    " + "documentation":"

    Keys that begin with the indicated prefix.

    " }, "Delimiter":{ "shape":"Delimiter", - "documentation":"

    A delimiter is a character you use to group keys.

    " + "documentation":"

    Causes keys that contain the same string between the prefix and the first occurrence of the delimiter to be rolled up into a single result element in the CommonPrefixes collection. These rolled-up keys are not returned elsewhere in the response. Each rolled-up result counts as only one return against the MaxKeys value.

    " }, "MaxKeys":{ "shape":"MaxKeys", @@ -5149,11 +5154,11 @@ }, "CommonPrefixes":{ "shape":"CommonPrefixList", - "documentation":"

    CommonPrefixes contains all (if there are any) keys between Prefix and the next occurrence of the string specified by delimiter

    " + "documentation":"

    All of the keys rolled up into a common prefix count as a single return when calculating the number of returns.

    A response can contain CommonPrefixes only if you specify a delimiter.

    CommonPrefixes contains all (if there are any) keys between Prefix and the next occurrence of the string specified by a delimiter.

    CommonPrefixes lists keys that act like subdirectories in the directory specified by Prefix.

    For example, if the prefix is notes/ and the delimiter is a slash (/) as in notes/summer/july, the common prefix is notes/summer/. All of the keys that roll up into a common prefix count as a single return when calculating the number of returns.

    " }, "EncodingType":{ "shape":"EncodingType", - "documentation":"

    Encoding type used by Amazon S3 to encode object keys in the response.

    " + "documentation":"

    Encoding type used by Amazon S3 to encode object key names in the XML response.

    If you specify the encoding-type request parameter, Amazon S3 includes this element in the response, and returns encoded key name values in the following response elements:

    Delimiter, Prefix, Key, and StartAfter.

    " }, "KeyCount":{ "shape":"KeyCount", @@ -5161,15 +5166,15 @@ }, "ContinuationToken":{ "shape":"Token", - "documentation":"

    ContinuationToken indicates Amazon S3 that the list is being continued on this bucket with a token. ContinuationToken is obfuscated and is not a real key

    " + "documentation":"

    If ContinuationToken was sent with the request, it is included in the response.

    " }, "NextContinuationToken":{ "shape":"NextToken", - "documentation":"

    NextContinuationToken is sent when isTruncated is true which means there are more keys in the bucket that can be listed. The next list requests to Amazon S3 can be continued with this NextContinuationToken. NextContinuationToken is obfuscated and is not a real key

    " + "documentation":"

    NextContinuationToken is sent when isTruncated is true, which means there are more keys in the bucket that can be listed. The next list requests to Amazon S3 can be continued with this NextContinuationToken. NextContinuationToken is obfuscated and is not a real key

    " }, "StartAfter":{ "shape":"StartAfter", - "documentation":"

    StartAfter is where you want Amazon S3 to start listing from. Amazon S3 starts listing after this specified key. StartAfter can be any key in the bucket

    " + "documentation":"

    If StartAfter was sent with the request, it is included in the response.

    " } } }, @@ -5179,7 +5184,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    Name of the bucket to list.

    ", + "documentation":"

    Bucket name to list.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -5209,19 +5214,19 @@ }, "ContinuationToken":{ "shape":"Token", - "documentation":"

    ContinuationToken indicates Amazon S3 that the list is being continued on this bucket with a token. ContinuationToken is obfuscated and is not a real key

    ", + "documentation":"

    ContinuationToken indicates Amazon S3 that the list is being continued on this bucket with a token. ContinuationToken is obfuscated and is not a real key.

    ", "location":"querystring", "locationName":"continuation-token" }, "FetchOwner":{ "shape":"FetchOwner", - "documentation":"

    The owner field is not present in listV2 by default, if you want to return owner field with each key in the result then set the fetch owner field to true

    ", + "documentation":"

    The owner field is not present in listV2 by default, if you want to return owner field with each key in the result then set the fetch owner field to true.

    ", "location":"querystring", "locationName":"fetch-owner" }, "StartAfter":{ "shape":"StartAfter", - "documentation":"

    StartAfter is where you want Amazon S3 to start listing from. Amazon S3 starts listing after this specified key. StartAfter can be any key in the bucket

    ", + "documentation":"

    StartAfter is where you want Amazon S3 to start listing from. Amazon S3 starts listing after this specified key. StartAfter can be any key in the bucket.

    ", "location":"querystring", "locationName":"start-after" }, @@ -5238,13 +5243,13 @@ "members":{ "AbortDate":{ "shape":"AbortDate", - "documentation":"

    Date when multipart upload will become eligible for abort operation by lifecycle.

    ", + "documentation":"

    If the bucket has a lifecycle rule configured with an action to abort incomplete multipart uploads and the prefix in the lifecycle rule matches the object name in the request, then the response includes this header indicating when the initiated multipart upload will become eligible for abort operation. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

    The response will also include the x-amz-abort-rule-id header that will provide the ID of the lifecycle configuration rule that defines this action.

    ", "location":"header", "locationName":"x-amz-abort-date" }, "AbortRuleId":{ "shape":"AbortRuleId", - "documentation":"

    Id of the lifecycle rule that makes a multipart upload eligible for abort operation.

    ", + "documentation":"

    This header is returned along with the x-amz-abort-date header. It identifies applicable lifecycle configuration rule that defines the action to abort incomplete multipart uploads.

    ", "location":"header", "locationName":"x-amz-abort-rule-id" }, @@ -5262,7 +5267,7 @@ }, "PartNumberMarker":{ "shape":"PartNumberMarker", - "documentation":"

    Part number after which listing begins.

    " + "documentation":"

    When a list is truncated, this element specifies the last part in the list, as well as the value to use for the part-number-marker request parameter in a subsequent request.

    " }, "NextPartNumberMarker":{ "shape":"NextPartNumberMarker", @@ -5274,24 +5279,24 @@ }, "IsTruncated":{ "shape":"IsTruncated", - "documentation":"

    Indicates whether the returned list of parts is truncated.

    " + "documentation":"

    Indicates whether the returned list of parts is truncated. A true value indicates that the list was truncated. A list can be truncated if the number of parts exceeds the limit returned in the MaxParts element.

    " }, "Parts":{ "shape":"Parts", - "documentation":"

    ", + "documentation":"

    Container for elements related to a particular part. A response can contain zero or more Part elements.

    ", "locationName":"Part" }, "Initiator":{ "shape":"Initiator", - "documentation":"

    Identifies who initiated the multipart upload.

    " + "documentation":"

    Container element that identifies who initiated the multipart upload. If the initiator is an AWS account, this element provides the same information as the Owner element. If the initiator is an IAM User, this element provides the user ARN and display name.

    " }, "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    Container element that identifies the object owner, after the object is created. If multipart upload is initiated by an IAM user, this element provides the parent account ID and display name.

    " }, "StorageClass":{ "shape":"StorageClass", - "documentation":"

    The class of storage used to store the object.

    " + "documentation":"

    Class of storage (STANDARD or REDUCED_REDUNDANCY) used to store the uploaded object.

    " }, "RequestCharged":{ "shape":"RequestCharged", @@ -5310,13 +5315,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Name of the bucket to which the parts are being uploaded.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Object key for which the multipart upload was initiated.

    ", "location":"uri", "locationName":"Key" }, @@ -5356,11 +5361,11 @@ "members":{ "TargetBucket":{ "shape":"TargetBucket", - "documentation":"

    Specifies the bucket where you want Amazon S3 to store server access logs. You can have your logs delivered to any bucket that you own, including the same bucket that is being logged. You can also configure multiple buckets to deliver their logs to the same target bucket. In this case you should choose a different TargetPrefix for each source bucket so that the delivered log files can be distinguished by key.

    " + "documentation":"

    Specifies the bucket where you want Amazon S3 to store server access logs. You can have your logs delivered to any bucket that you own, including the same bucket that is being logged. You can also configure multiple buckets to deliver their logs to the same target bucket. In this case, you should choose a different TargetPrefix for each source bucket so that the delivered log files can be distinguished by key.

    " }, "TargetGrants":{ "shape":"TargetGrants", - "documentation":"

    " + "documentation":"

    Container for granting information.

    " }, "TargetPrefix":{ "shape":"TargetPrefix", @@ -5407,17 +5412,35 @@ "members":{ "Name":{ "shape":"MetadataKey", - "documentation":"

    " + "documentation":"

    Name of the Object.

    " }, "Value":{ "shape":"MetadataValue", - "documentation":"

    " + "documentation":"

    Value of the Object.

    " } }, "documentation":"

    A metadata key-value pair to store with an object.

    " }, "MetadataKey":{"type":"string"}, "MetadataValue":{"type":"string"}, + "Metrics":{ + "type":"structure", + "required":[ + "Status", + "EventThreshold" + ], + "members":{ + "Status":{ + "shape":"MetricsStatus", + "documentation":"

    Specifies whether the replication metrics are enabled.

    " + }, + "EventThreshold":{ + "shape":"ReplicationTimeValue", + "documentation":"

    A container specifying the time threshold for emitting the s3:Replication:OperationMissedThreshold event.

    " + } + }, + "documentation":"

    A container specifying replication metrics-related settings enabling metrics and Amazon S3 events for S3 Replication Time Control (S3 RTC). Must be specified together with a ReplicationTime block.

    " + }, "MetricsAndOperator":{ "type":"structure", "members":{ @@ -5432,7 +5455,7 @@ "locationName":"Tag" } }, - "documentation":"

    " + "documentation":"

    A conjunction (logical AND) of predicates, which is used in evaluating a metrics filter. The operator must have at least two predicates, and an object must match all of the predicates in order for the filter to apply.

    " }, "MetricsConfiguration":{ "type":"structure", @@ -5470,9 +5493,17 @@ "documentation":"

    A conjunction (logical AND) of predicates, which is used in evaluating a metrics filter. The operator must have at least two predicates, and an object must match all of the predicates in order for the filter to apply.

    " } }, - "documentation":"

    " + "documentation":"

    Specifies a metrics configuration filter. The metrics configuration only includes objects that meet the filter's criteria. A filter must be a prefix, a tag, or a conjunction (MetricsAndOperator).

    " }, "MetricsId":{"type":"string"}, + "MetricsStatus":{ + "type":"string", + "enum":[ + "Enabled", + "Disabled" + ] + }, + "Minutes":{"type":"integer"}, "MissingMeta":{"type":"integer"}, "MultipartUpload":{ "type":"structure", @@ -5495,14 +5526,14 @@ }, "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    Specifies the owner of the object that is part of the multipart upload.

    " }, "Initiator":{ "shape":"Initiator", "documentation":"

    Identifies who initiated the multipart upload.

    " } }, - "documentation":"

    " + "documentation":"

    Container for the MultipartUpload for the Amazon S3 object.

    " }, "MultipartUploadId":{"type":"string"}, "MultipartUploadList":{ @@ -5542,7 +5573,7 @@ "members":{ "NoncurrentDays":{ "shape":"Days", - "documentation":"

    Specifies the number of days an object is noncurrent before Amazon S3 can perform the associated action. For information about the noncurrent days calculations, see How Amazon S3 Calculates When an Object Became Noncurrent in the Amazon Simple Storage Service Developer Guide.

    " + "documentation":"

    Specifies the number of days an object is noncurrent before Amazon S3 can perform the associated action. For information about the noncurrent days calculations, see How Amazon S3 Calculates When an Object Became Noncurrent in the Amazon Simple Storage Service Developer Guide.

    " } }, "documentation":"

    Specifies when noncurrent object versions expire. Upon expiration, Amazon S3 permanently deletes the noncurrent object versions. You set this lifecycle configuration action on a bucket that has versioning enabled (or suspended) to request that Amazon S3 delete noncurrent object versions at a specific period in the object's lifetime.

    " @@ -5552,7 +5583,7 @@ "members":{ "NoncurrentDays":{ "shape":"Days", - "documentation":"

    Specifies the number of days an object is noncurrent before Amazon S3 can perform the associated action. For information about the noncurrent days calculations, see How Amazon S3 Calculates When an Object Became Noncurrent in the Amazon Simple Storage Service Developer Guide.

    " + "documentation":"

    Specifies the number of days an object is noncurrent before Amazon S3 can perform the associated action. For information about the noncurrent days calculations, see How Amazon S3 Calculates How Long an Object Has Been Noncurrent in the Amazon Simple Storage Service Developer Guide.

    " }, "StorageClass":{ "shape":"TransitionStorageClass", @@ -5592,15 +5623,15 @@ "members":{ "TopicConfiguration":{ "shape":"TopicConfigurationDeprecated", - "documentation":"

    " + "documentation":"

    This data type is deprecated. A container for specifying the configuration for publication of messages to an Amazon Simple Notification Service (Amazon SNS) topic when Amazon S3 detects specified events.

    " }, "QueueConfiguration":{ "shape":"QueueConfigurationDeprecated", - "documentation":"

    " + "documentation":"

    This data type is deprecated. This data type specifies the configuration for publishing messages to an Amazon Simple Queue Service (Amazon SQS) queue when Amazon S3 detects specified events.

    " }, "CloudFunctionConfiguration":{ "shape":"CloudFunctionConfiguration", - "documentation":"

    " + "documentation":"

    Container for specifying the AWS Lambda notification configuration.

    " } } }, @@ -5609,7 +5640,6 @@ "members":{ "Key":{ "shape":"S3KeyFilter", - "documentation":"

    ", "locationName":"S3Key" } }, @@ -5624,19 +5654,19 @@ "members":{ "Key":{ "shape":"ObjectKey", - "documentation":"

    " + "documentation":"

    The name that you assign to an object. You use the object key to retrieve the object.

    " }, "LastModified":{ "shape":"LastModified", - "documentation":"

    " + "documentation":"

    The date the Object was Last Modified

    " }, "ETag":{ "shape":"ETag", - "documentation":"

    " + "documentation":"

    The entity tag is an MD5 hash of the object. ETag reflects only changes to the contents of an object, not its metadata.

    " }, "Size":{ "shape":"Size", - "documentation":"

    " + "documentation":"

    Size in bytes of the object

    " }, "StorageClass":{ "shape":"ObjectStorageClass", @@ -5644,16 +5674,16 @@ }, "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    The owner of the object

    " } }, - "documentation":"

    " + "documentation":"

    An object consists of data and its descriptive metadata.

    " }, "ObjectAlreadyInActiveTierError":{ "type":"structure", "members":{ }, - "documentation":"

    This operation is not allowed against this storage tier

    ", + "documentation":"

    This operation is not allowed against this storage tier.

    ", "exception":true }, "ObjectCannedACL":{ @@ -5681,7 +5711,7 @@ "documentation":"

    VersionId for the specific version of the object to delete.

    " } }, - "documentation":"

    " + "documentation":"

    Object Identifier is unique value to identify objects.

    " }, "ObjectIdentifierList":{ "type":"list", @@ -5702,14 +5732,14 @@ "members":{ "ObjectLockEnabled":{ "shape":"ObjectLockEnabled", - "documentation":"

    Indicates whether this bucket has an object lock configuration enabled.

    " + "documentation":"

    Indicates whether this bucket has an Object Lock configuration enabled.

    " }, "Rule":{ "shape":"ObjectLockRule", - "documentation":"

    The object lock rule in place for the specified object.

    " + "documentation":"

    The Object Lock rule in place for the specified object.

    " } }, - "documentation":"

    The container element for object lock configuration parameters.

    " + "documentation":"

    The container element for Object Lock configuration parameters.

    " }, "ObjectLockEnabled":{ "type":"string", @@ -5753,7 +5783,7 @@ }, "RetainUntilDate":{ "shape":"Date", - "documentation":"

    The date on which this object lock retention expires.

    " + "documentation":"

    The date on which this Object Lock Retention will expire.

    " } }, "documentation":"

    A Retention configuration for an object.

    " @@ -5773,14 +5803,14 @@ "documentation":"

    The default retention period that you want to apply to new objects placed in the specified bucket.

    " } }, - "documentation":"

    The container element for an object lock rule.

    " + "documentation":"

    The container element for an Object Lock rule.

    " }, "ObjectLockToken":{"type":"string"}, "ObjectNotInActiveTierError":{ "type":"structure", "members":{ }, - "documentation":"

    The source object of the COPY operation is not in the active tier and is only stored in Amazon Glacier.

    ", + "documentation":"

    The source object of the COPY operation is not in the active tier and is only stored in Amazon S3 Glacier.

    ", "exception":true }, "ObjectStorageClass":{ @@ -5800,7 +5830,7 @@ "members":{ "ETag":{ "shape":"ETag", - "documentation":"

    " + "documentation":"

    The entity tag is an MD5 hash of that version of the object.

    " }, "Size":{ "shape":"Size", @@ -5828,10 +5858,10 @@ }, "Owner":{ "shape":"Owner", - "documentation":"

    " + "documentation":"

    Specifies the owner of the object.

    " } }, - "documentation":"

    " + "documentation":"

    The version of an object.

    " }, "ObjectVersionId":{"type":"string"}, "ObjectVersionList":{ @@ -5872,14 +5902,14 @@ "members":{ "DisplayName":{ "shape":"DisplayName", - "documentation":"

    " + "documentation":"

    Container for the display name of the owner.

    " }, "ID":{ "shape":"ID", - "documentation":"

    " + "documentation":"

    Container for the ID of the owner.

    " } }, - "documentation":"

    " + "documentation":"

    Container for the owner's display name and ID.

    " }, "OwnerOverride":{ "type":"string", @@ -5889,7 +5919,7 @@ "type":"structure", "members":{ }, - "documentation":"

    " + "documentation":"

    Container for Parquet.

    " }, "Part":{ "type":"structure", @@ -5911,7 +5941,7 @@ "documentation":"

    Size in bytes of the uploaded part data.

    " } }, - "documentation":"

    " + "documentation":"

    Container for elements related to a part.

    " }, "PartNumber":{"type":"integer"}, "PartNumberMarker":{"type":"integer"}, @@ -5968,7 +5998,7 @@ "documentation":"

    The current number of bytes of records payload data returned.

    " } }, - "documentation":"

    " + "documentation":"

    This data type contains information about progress of an operation.

    " }, "ProgressEvent":{ "type":"structure", @@ -5979,7 +6009,7 @@ "eventpayload":true } }, - "documentation":"

    ", + "documentation":"

    This data type contains information about the progress event of an operation.

    ", "event":true }, "Protocol":{ @@ -5994,7 +6024,7 @@ "members":{ "BlockPublicAcls":{ "shape":"Setting", - "documentation":"

    Specifies whether Amazon S3 should block public access control lists (ACLs) for this bucket and objects in this bucket. Setting this element to TRUE causes the following behavior:

    • PUT Bucket acl and PUT Object acl calls fail if the specified ACL is public.

    • PUT Object calls fail if the request includes a public ACL.

    Enabling this setting doesn't affect existing policies or ACLs.

    ", + "documentation":"

    Specifies whether Amazon S3 should block public access control lists (ACLs) for this bucket and objects in this bucket. Setting this element to TRUE causes the following behavior:

    • PUT Bucket acl and PUT Object acl calls fail if the specified ACL is public.

    • PUT Object calls fail if the request includes a public ACL.

    • PUT Bucket calls fail if the request includes a public ACL.

    Enabling this setting doesn't affect existing policies or ACLs.

    ", "locationName":"BlockPublicAcls" }, "IgnorePublicAcls":{ @@ -6013,7 +6043,7 @@ "locationName":"RestrictPublicBuckets" } }, - "documentation":"

    Specifies the Block Public Access configuration for an Amazon S3 bucket.

    " + "documentation":"

    The PublicAccessBlock configuration that you want to apply to this Amazon S3 bucket. You can enable the configuration options in any combination. For more information about when Amazon S3 considers a bucket or object public, see The Meaning of \"Public\" in the Amazon Simple Storage Service Developer Guide.

    " }, "PutBucketAccelerateConfigurationRequest":{ "type":"structure", @@ -6030,7 +6060,7 @@ }, "AccelerateConfiguration":{ "shape":"AccelerateConfiguration", - "documentation":"

    Specifies the Accelerate Configuration you want to set for the bucket.

    ", + "documentation":"

    Container for setting the transfer acceleration state.

    ", "locationName":"AccelerateConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6055,13 +6085,13 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket to which to apply the ACL.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. This header must be used as a message integrity check to verify that the request body was not corrupted in transit. For more information, go to RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" }, @@ -6136,19 +6166,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    Specifies the bucket impacted by the corsconfiguration.

    ", "location":"uri", "locationName":"Bucket" }, "CORSConfiguration":{ "shape":"CORSConfiguration", - "documentation":"

    ", + "documentation":"

    Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more information, see Enabling Cross-Origin Resource Sharing in the Amazon Simple Storage Service Developer Guide.

    ", "locationName":"CORSConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. This header must be used as a message integrity check to verify that the request body was not corrupted in transit. For more information, go to RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" } @@ -6164,7 +6194,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    Specifies default encryption for a bucket using server-side encryption with Amazon S3-managed keys (SSE-S3) or AWS KMS-managed keys (SSE-KMS). For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption in the Amazon Simple Storage Service Developer Guide.

    ", + "documentation":"

    Specifies default encryption for a bucket using server-side encryption with Amazon S3-managed keys (SSE-S3) or customer master keys stored in AWS KMS (SSE-KMS). For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -6176,7 +6206,6 @@ }, "ServerSideEncryptionConfiguration":{ "shape":"ServerSideEncryptionConfiguration", - "documentation":"

    ", "locationName":"ServerSideEncryptionConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6218,13 +6247,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to set the configuration.

    ", "location":"uri", "locationName":"Bucket" }, "LifecycleConfiguration":{ "shape":"BucketLifecycleConfiguration", - "documentation":"

    ", + "documentation":"

    Container for lifecycle rules. You can add as many as 1,000 rules.

    ", "locationName":"LifecycleConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6265,19 +6294,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket for which to set the logging parameters.

    ", "location":"uri", "locationName":"Bucket" }, "BucketLoggingStatus":{ "shape":"BucketLoggingStatus", - "documentation":"

    ", + "documentation":"

    Container for logging status information.

    ", "locationName":"BucketLoggingStatus", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The MD5 hash of the PutBucketLogging request body.

    ", "location":"header", "locationName":"Content-MD5" } @@ -6322,13 +6351,12 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket.

    ", "location":"uri", "locationName":"Bucket" }, "NotificationConfiguration":{ "shape":"NotificationConfiguration", - "documentation":"

    ", "locationName":"NotificationConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6344,19 +6372,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The MD5 hash of the PutPublicAccessBlock request body.

    ", "location":"header", "locationName":"Content-MD5" }, "NotificationConfiguration":{ "shape":"NotificationConfigurationDeprecated", - "documentation":"

    ", + "documentation":"

    The container for the configuration.

    ", "locationName":"NotificationConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6372,13 +6400,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The MD5 hash of the request body.

    ", "location":"header", "locationName":"Content-MD5" }, @@ -6404,25 +6432,24 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The name of the bucket

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message integrity check to verify that the request body was not corrupted in transit.

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message integrity check to verify that the request body was not corrupted in transit. For more information, see RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" }, "ReplicationConfiguration":{ "shape":"ReplicationConfiguration", - "documentation":"

    ", "locationName":"ReplicationConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, "Token":{ "shape":"ObjectLockToken", - "documentation":"

    A token that allows Amazon S3 object lock to be enabled for an existing bucket.

    ", + "documentation":"

    ", "location":"header", "locationName":"x-amz-bucket-object-lock-token" } @@ -6438,19 +6465,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    >The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message integrity check to verify that the request body was not corrupted in transit. For more information, see RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" }, "RequestPaymentConfiguration":{ "shape":"RequestPaymentConfiguration", - "documentation":"

    ", + "documentation":"

    Container for Payer.

    ", "locationName":"RequestPaymentConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6466,19 +6493,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message integrity check to verify that the request body was not corrupted in transit. For more information, see RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" }, "Tagging":{ "shape":"Tagging", - "documentation":"

    ", + "documentation":"

    Container for the TagSet and Tag elements.

    ", "locationName":"Tagging", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6494,13 +6521,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    >The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message integrity check to verify that the request body was not corrupted in transit. For more information, see RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" }, @@ -6512,7 +6539,7 @@ }, "VersioningConfiguration":{ "shape":"VersioningConfiguration", - "documentation":"

    ", + "documentation":"

    Container for setting the versioning state.

    ", "locationName":"VersioningConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6528,19 +6555,19 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message integrity check to verify that the request body was not corrupted in transit. For more information, see RFC 1864.

    ", "location":"header", "locationName":"Content-MD5" }, "WebsiteConfiguration":{ "shape":"WebsiteConfiguration", - "documentation":"

    ", + "documentation":"

    Container for the request.

    ", "locationName":"WebsiteConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -6566,7 +6593,7 @@ "members":{ "ACL":{ "shape":"ObjectCannedACL", - "documentation":"

    The canned ACL to apply to the object.

    ", + "documentation":"

    The canned ACL to apply to the object. For more information, see Canned ACL.

    ", "location":"header", "locationName":"x-amz-acl" }, @@ -6578,13 +6605,13 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name that contains the object to which you want to attach the ACL.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the data. This header must be used as a message integrity check to verify that the request body was not corrupted in transit. For more information, go to RFC 1864.>

    ", "location":"header", "locationName":"Content-MD5" }, @@ -6620,7 +6647,7 @@ }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Key for which the PUT operation was initiated.

    ", "location":"uri", "locationName":"Key" }, @@ -6657,7 +6684,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket containing the object that you want to place a Legal Hold on.

    ", + "documentation":"

    The bucket name containing the object that you want to place a Legal Hold on.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -6709,13 +6736,13 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket whose object lock configuration you want to create or replace.

    ", + "documentation":"

    The bucket whose Object Lock configuration you want to create or replace.

    ", "location":"uri", "locationName":"Bucket" }, "ObjectLockConfiguration":{ "shape":"ObjectLockConfiguration", - "documentation":"

    The object lock configuration that you want to apply to the specified bucket.

    ", + "documentation":"

    The Object Lock configuration that you want to apply to the specified bucket.

    ", "locationName":"ObjectLockConfiguration", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, @@ -6726,7 +6753,7 @@ }, "Token":{ "shape":"ObjectLockToken", - "documentation":"

    A token to allow Amazon S3 object lock to be enabled for an existing bucket.

    ", + "documentation":"

    A token to allow Object Lock to be enabled for an existing bucket.

    ", "location":"header", "locationName":"x-amz-bucket-object-lock-token" }, @@ -6744,7 +6771,7 @@ "members":{ "Expiration":{ "shape":"Expiration", - "documentation":"

    If the object expiration is configured, this will contain the expiration date (expiry-date) and rule ID (rule-id). The value of rule-id is URL encoded.

    ", + "documentation":"

    If the expiration is configured for the object (see PutBucketLifecycleConfiguration), the response includes this header. It includes the expiry-date and rule-id key-value pairs that provide information about object expiration. The value of the rule-id is URL encoded.

    ", "location":"header", "locationName":"x-amz-expiration" }, @@ -6756,7 +6783,7 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    If you specified server-side encryption either with an AWS KMS customer master key (CMK) or Amazon S3-managed encryption key in your PUT request, the response includes this header. It confirms the encryption algorithm that Amazon S3 used to encrypt the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -6774,13 +6801,13 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If x-amz-server-side-encryption is present and has the value of aws:kms, this header specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -6806,7 +6833,7 @@ "members":{ "ACL":{ "shape":"ObjectCannedACL", - "documentation":"

    The canned ACL to apply to the object.

    ", + "documentation":"

    The canned ACL to apply to the object. For more information, see Canned ACL.

    ", "location":"header", "locationName":"x-amz-acl" }, @@ -6817,25 +6844,25 @@ }, "Bucket":{ "shape":"BucketName", - "documentation":"

    Name of the bucket to which the PUT operation was initiated.

    ", + "documentation":"

    Bucket name to which the PUT operation was initiated.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "CacheControl":{ "shape":"CacheControl", - "documentation":"

    Specifies caching behavior along the request/reply chain.

    ", + "documentation":"

    Can be used to specify caching behavior along the request/reply chain. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.

    ", "location":"header", "locationName":"Cache-Control" }, "ContentDisposition":{ "shape":"ContentDisposition", - "documentation":"

    Specifies presentational information for the object.

    ", + "documentation":"

    Specifies presentational information for the object. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1.

    ", "location":"header", "locationName":"Content-Disposition" }, "ContentEncoding":{ "shape":"ContentEncoding", - "documentation":"

    Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.

    ", + "documentation":"

    Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.

    ", "location":"header", "locationName":"Content-Encoding" }, @@ -6847,25 +6874,25 @@ }, "ContentLength":{ "shape":"ContentLength", - "documentation":"

    Size of the body in bytes. This parameter is useful when the size of the body cannot be determined automatically.

    ", + "documentation":"

    Size of the body in bytes. This parameter is useful when the size of the body cannot be determined automatically. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13.

    ", "location":"header", "locationName":"Content-Length" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    The base64-encoded 128-bit MD5 digest of the part data. This parameter is auto-populated when using the command from the CLI. This parameted is required if object lock parameters are specified.

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the message (without the headers) according to RFC 1864. This header can be used as a message integrity check to verify that the data is the same data that was originally sent. Although it is optional, we recommend using the Content-MD5 mechanism as an end-to-end integrity check. For more information about REST request authentication, see REST Authentication.

    ", "location":"header", "locationName":"Content-MD5" }, "ContentType":{ "shape":"ContentType", - "documentation":"

    A standard MIME type describing the format of the object data.

    ", + "documentation":"

    A standard MIME type describing the format of the contents. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17.

    ", "location":"header", "locationName":"Content-Type" }, "Expires":{ "shape":"Expires", - "documentation":"

    The date and time at which the object is no longer cacheable.

    ", + "documentation":"

    The date and time at which the object is no longer cacheable. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21.

    ", "location":"header", "locationName":"Expires" }, @@ -6907,43 +6934,43 @@ }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, "StorageClass":{ "shape":"StorageClass", - "documentation":"

    The type of storage to use for the object. Defaults to 'STANDARD'.

    ", + "documentation":"

    If you don't specify, Standard is the default storage class. Amazon S3 supports other storage classes.

    ", "location":"header", "locationName":"x-amz-storage-class" }, "WebsiteRedirectLocation":{ "shape":"WebsiteRedirectLocation", - "documentation":"

    If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata.

    ", + "documentation":"

    If the bucket is configured as a website, redirects requests for this object to another object in the same bucket or to an external URL. Amazon S3 stores the value of this header in the object metadata. For information about object metadata, see Object Key and Metadata.

    In the following example, the request header sets the redirect to an object (anotherPage.html) in the same bucket:

    x-amz-website-redirect-location: /anotherPage.html

    In the following example, the request header sets the object redirect to another website:

    x-amz-website-redirect-location: http://www.example.com/

    For more information about website hosting in Amazon S3, see Hosting Websites on Amazon S3 and How to Configure Website Page Redirects.

    ", "location":"header", "locationName":"x-amz-website-redirect-location" }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    Specifies the AWS KMS key ID to use for object encryption. All GET and PUT requests for an object protected by AWS KMS will fail if not made via SSL or using SigV4. Documentation on configuring any of the officially supported AWS SDKs and CLI can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version

    ", + "documentation":"

    If x-amz-server-side-encryption is present and has the value of aws:kms, this header specifies the ID of the AWS Key Management Service (AWS KMS) symmetrical customer managed customer master key (CMK) that was used for the object.

    If the value of x-amz-server-side-encryption is aws:kms, this header specifies the ID of the symmetric customer managed AWS KMS CMK that will be used for the object. If you specify x-amz-server-side-encryption:aws:kms, but do not provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the AWS managed CMK in AWS to protect the data.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -6966,19 +6993,19 @@ }, "ObjectLockMode":{ "shape":"ObjectLockMode", - "documentation":"

    The object lock mode that you want to apply to this object.

    ", + "documentation":"

    The Object Lock mode that you want to apply to this object.

    ", "location":"header", "locationName":"x-amz-object-lock-mode" }, "ObjectLockRetainUntilDate":{ "shape":"ObjectLockRetainUntilDate", - "documentation":"

    The date and time when you want this object's object lock to expire.

    ", + "documentation":"

    The date and time when you want this object's Object Lock to expire.

    ", "location":"header", "locationName":"x-amz-object-lock-retain-until-date" }, "ObjectLockLegalHoldStatus":{ "shape":"ObjectLockLegalHoldStatus", - "documentation":"

    The Legal Hold status that you want to apply to the specified object.

    ", + "documentation":"

    Specifies whether a legal hold will be applied to this object. For more information about S3 Object Lock, see Object Lock.

    ", "location":"header", "locationName":"x-amz-object-lock-legal-hold" } @@ -7004,7 +7031,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    The bucket that contains the object you want to apply this Object Retention configuration to.

    ", + "documentation":"

    The bucket name that contains the object you want to apply this Object Retention configuration to.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, @@ -7033,7 +7060,7 @@ }, "BypassGovernanceRetention":{ "shape":"BypassGovernanceRetention", - "documentation":"

    Indicates whether this operation should bypass Governance-mode restrictions.j

    ", + "documentation":"

    Indicates whether this operation should bypass Governance-mode restrictions.

    ", "location":"header", "locationName":"x-amz-bypass-governance-retention" }, @@ -7051,7 +7078,7 @@ "members":{ "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    ", + "documentation":"

    The versionId of the object the tag-set was added to.

    ", "location":"header", "locationName":"x-amz-version-id" } @@ -7067,31 +7094,31 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name containing the object.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Name of the tag.

    ", "location":"uri", "locationName":"Key" }, "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    ", + "documentation":"

    The versionId of the object that the tag-set will be added to.

    ", "location":"querystring", "locationName":"versionId" }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    ", + "documentation":"

    The MD5 hash for the request body.

    ", "location":"header", "locationName":"Content-MD5" }, "Tagging":{ "shape":"Tagging", - "documentation":"

    ", + "documentation":"

    Container for the TagSet and Tag elements

    ", "locationName":"Tagging", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} } @@ -7142,13 +7169,10 @@ }, "Events":{ "shape":"EventList", - "documentation":"

    ", + "documentation":"

    A collection of bucket events for which to send notifications

    ", "locationName":"Event" }, - "Filter":{ - "shape":"NotificationConfigurationFilter", - "documentation":"

    " - } + "Filter":{"shape":"NotificationConfigurationFilter"} }, "documentation":"

    Specifies the configuration for publishing messages to an Amazon Simple Queue Service (Amazon SQS) queue when Amazon S3 detects specified events.

    " }, @@ -7162,15 +7186,15 @@ }, "Events":{ "shape":"EventList", - "documentation":"

    ", + "documentation":"

    A collection of bucket events for which to send notifications

    ", "locationName":"Event" }, "Queue":{ "shape":"QueueArn", - "documentation":"

    " + "documentation":"

    The Amazon Resource Name (ARN) of the Amazon SQS queue to which Amazon S3 publishes a message when it detects events of the specified type.

    " } }, - "documentation":"

    " + "documentation":"

    This data type is deprecated. Use QueueConfiguration for the same purposes. This data type specifies the configuration for publishing messages to an Amazon Simple Queue Service (Amazon SQS) queue when Amazon S3 detects specified events.

    " }, "QueueConfigurationList":{ "type":"list", @@ -7198,7 +7222,7 @@ "eventpayload":true } }, - "documentation":"

    ", + "documentation":"

    The container for the records event.

    ", "event":true }, "Redirect":{ @@ -7254,7 +7278,7 @@ "members":{ "Role":{ "shape":"Role", - "documentation":"

    The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that Amazon S3 assumes when replicating objects. For more information, see How to Set Up Cross-Region Replication in the Amazon Simple Storage Service Developer Guide.

    " + "documentation":"

    The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that Amazon S3 assumes when replicating objects. For more information, see How to Set Up Replication in the Amazon Simple Storage Service Developer Guide.

    " }, "Rules":{ "shape":"ReplicationRules", @@ -7277,33 +7301,31 @@ }, "Priority":{ "shape":"Priority", - "documentation":"

    The priority associated with the rule. If you specify multiple rules in a replication configuration, Amazon S3 prioritizes the rules to prevent conflicts when filtering. If two or more rules identify the same object based on a specified filter, the rule with higher priority takes precedence. For example:

    • Same object quality prefix based filter criteria If prefixes you specified in multiple rules overlap

    • Same object qualify tag based filter criteria specified in multiple rules

    For more information, see Cross-Region Replication (CRR) in the Amazon S3 Developer Guide.

    " + "documentation":"

    The priority associated with the rule. If you specify multiple rules in a replication configuration, Amazon S3 prioritizes the rules to prevent conflicts when filtering. If two or more rules identify the same object based on a specified filter, the rule with higher priority takes precedence. For example:

    • Same object quality prefix-based filter criteria if prefixes you specified in multiple rules overlap

    • Same object qualify tag-based filter criteria specified in multiple rules

    For more information, see Replication in the Amazon Simple Storage Service Developer Guide.

    " }, "Prefix":{ "shape":"Prefix", - "documentation":"

    An object keyname prefix that identifies the object or objects to which the rule applies. The maximum prefix length is 1,024 characters. To include all objects in a bucket, specify an empty string.

    ", + "documentation":"

    An object key name prefix that identifies the object or objects to which the rule applies. The maximum prefix length is 1,024 characters. To include all objects in a bucket, specify an empty string.

    ", "deprecated":true }, - "Filter":{ - "shape":"ReplicationRuleFilter", - "documentation":"

    " - }, + "Filter":{"shape":"ReplicationRuleFilter"}, "Status":{ "shape":"ReplicationRuleStatus", "documentation":"

    Specifies whether the rule is enabled.

    " }, "SourceSelectionCriteria":{ "shape":"SourceSelectionCriteria", - "documentation":"

    A container that describes additional filters for identifying the source objects that you want to replicate. You can choose to enable or disable the replication of these objects. Currently, Amazon S3 supports only the filter that you can specify for objects created with server-side encryption using an AWS KMS-Managed Key (SSE-KMS).

    " + "documentation":"

    A container that describes additional filters for identifying the source objects that you want to replicate. You can choose to enable or disable the replication of these objects. Currently, Amazon S3 supports only the filter that you can specify for objects created with server-side encryption using a customer master key (CMK) stored in AWS Key Management Service (SSE-KMS).

    " + }, + "ExistingObjectReplication":{ + "shape":"ExistingObjectReplication", + "documentation":"

    " }, "Destination":{ "shape":"Destination", - "documentation":"

    A container for information about the replication destination.

    " + "documentation":"

    A container for information about the replication destination and its configurations including enabling the S3 Replication Time Control (S3 RTC).

    " }, - "DeleteMarkerReplication":{ - "shape":"DeleteMarkerReplication", - "documentation":"

    " - } + "DeleteMarkerReplication":{"shape":"DeleteMarkerReplication"} }, "documentation":"

    Specifies which Amazon S3 objects to replicate and where to store the replicas.

    " }, @@ -7312,23 +7334,23 @@ "members":{ "Prefix":{ "shape":"Prefix", - "documentation":"

    " + "documentation":"

    An object key name prefix that identifies the subset of objects to which the rule applies.

    " }, "Tags":{ "shape":"TagSet", - "documentation":"

    ", + "documentation":"

    An array of tags containing key and value pairs.

    ", "flattened":true, "locationName":"Tag" } }, - "documentation":"

    " + "documentation":"

    A container for specifying rule filters. The filters determine the subset of objects to which the rule applies. This element is required only if you specify more than one filter.

    For example:

    • If you specify both a Prefix and a Tag filter, wrap these filters in an And tag.

    • If you specify a filter based on multiple tags, wrap the Tag elements in an And tag

    " }, "ReplicationRuleFilter":{ "type":"structure", "members":{ "Prefix":{ "shape":"Prefix", - "documentation":"

    An object keyname prefix that identifies the subset of objects to which the rule applies.

    " + "documentation":"

    An object key name prefix that identifies the subset of objects to which the rule applies.

    " }, "Tag":{ "shape":"Tag", @@ -7362,6 +7384,41 @@ "REPLICA" ] }, + "ReplicationTime":{ + "type":"structure", + "required":[ + "Status", + "Time" + ], + "members":{ + "Status":{ + "shape":"ReplicationTimeStatus", + "documentation":"

    Specifies whether the replication time is enabled.

    " + }, + "Time":{ + "shape":"ReplicationTimeValue", + "documentation":"

    A container specifying the time by which replication should be complete for all objects and operations on objects.

    " + } + }, + "documentation":"

    A container specifying S3 Replication Time Control (S3 RTC) related information, including whether S3 RTC is enabled and the time when all objects and operations on objects must be replicated. Must be specified together with a Metrics block.

    " + }, + "ReplicationTimeStatus":{ + "type":"string", + "enum":[ + "Enabled", + "Disabled" + ] + }, + "ReplicationTimeValue":{ + "type":"structure", + "members":{ + "Minutes":{ + "shape":"Minutes", + "documentation":"

    Contains an integer specifying time in minutes.

    Valid values: 15 minutes.

    " + } + }, + "documentation":"

    A container specifying the time value for S3 Replication Time Control (S3 RTC) and replication metrics EventThreshold.

    " + }, "RequestCharged":{ "type":"string", "documentation":"

    If present, indicates that the requester was successfully charged for the request.

    ", @@ -7369,7 +7426,7 @@ }, "RequestPayer":{ "type":"string", - "documentation":"

    Confirms that the requester knows that she or he will be charged for the request. Bucket owners need not specify this parameter in their requests. Documentation on downloading objects from requester pays buckets can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html

    ", + "documentation":"

    Confirms that the requester knows that they will be charged for the request. Bucket owners need not specify this parameter in their requests. For information about downloading objects from requester pays buckets, see Downloading Objects in Requestor Pays Buckets in the Amazon S3 Developer Guide.

    ", "enum":["requester"] }, "RequestPaymentConfiguration":{ @@ -7381,7 +7438,7 @@ "documentation":"

    Specifies who pays for the download and request fees.

    " } }, - "documentation":"

    " + "documentation":"

    Container for Payer.

    " }, "RequestProgress":{ "type":"structure", @@ -7391,7 +7448,7 @@ "documentation":"

    Specifies whether periodic QueryProgress frames should be sent. Valid values: TRUE, FALSE. Default value: FALSE.

    " } }, - "documentation":"

    " + "documentation":"

    Container for specifying if periodic QueryProgress messages should be sent.

    " }, "ResponseCacheControl":{"type":"string"}, "ResponseContentDisposition":{"type":"string"}, @@ -7425,25 +7482,24 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name or containing the object to restore.

    When using this API with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this operation using an access point through the AWS SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using Access Points in the Amazon Simple Storage Service Developer Guide.

    ", "location":"uri", "locationName":"Bucket" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Object key for which the operation was initiated.

    ", "location":"uri", "locationName":"Key" }, "VersionId":{ "shape":"ObjectVersionId", - "documentation":"

    ", + "documentation":"

    VersionId used to reference a specific version of the object.

    ", "location":"querystring", "locationName":"versionId" }, "RestoreRequest":{ "shape":"RestoreRequest", - "documentation":"

    ", "locationName":"RestoreRequest", "xmlNamespace":{"uri":"http://s3.amazonaws.com/doc/2006-03-01/"} }, @@ -7461,11 +7517,11 @@ "members":{ "Days":{ "shape":"Days", - "documentation":"

    Lifetime of the active copy in days. Do not use with restores that specify OutputLocation.

    " + "documentation":"

    Lifetime of the active copy in days. Do not use with restores that specify OutputLocation.

    " }, "GlacierJobParameters":{ "shape":"GlacierJobParameters", - "documentation":"

    Glacier related parameters pertaining to this job. Do not use with restores that specify OutputLocation.

    " + "documentation":"

    Glacier related parameters pertaining to this job. Do not use with restores that specify OutputLocation.

    " }, "Type":{ "shape":"RestoreRequestType", @@ -7526,7 +7582,7 @@ "members":{ "Expiration":{ "shape":"LifecycleExpiration", - "documentation":"

    " + "documentation":"

    Specifies the expiration for the lifecycle of the object.

    " }, "ID":{ "shape":"ID", @@ -7542,20 +7598,11 @@ }, "Transition":{ "shape":"Transition", - "documentation":"

    " - }, - "NoncurrentVersionTransition":{ - "shape":"NoncurrentVersionTransition", - "documentation":"

    " - }, - "NoncurrentVersionExpiration":{ - "shape":"NoncurrentVersionExpiration", - "documentation":"

    " + "documentation":"

    Specifies when an object transitions to a specified storage class.

    " }, - "AbortIncompleteMultipartUpload":{ - "shape":"AbortIncompleteMultipartUpload", - "documentation":"

    " - } + "NoncurrentVersionTransition":{"shape":"NoncurrentVersionTransition"}, + "NoncurrentVersionExpiration":{"shape":"NoncurrentVersionExpiration"}, + "AbortIncompleteMultipartUpload":{"shape":"AbortIncompleteMultipartUpload"} }, "documentation":"

    Specifies lifecycle rules for an Amazon S3 bucket. For more information, see PUT Bucket lifecycle in the Amazon Simple Storage Service API Reference.

    " }, @@ -7569,7 +7616,6 @@ "members":{ "FilterRules":{ "shape":"FilterRuleList", - "documentation":"

    ", "locationName":"FilterRule" } }, @@ -7590,10 +7636,7 @@ "shape":"LocationPrefix", "documentation":"

    The prefix that is prepended to the restore results for this request.

    " }, - "Encryption":{ - "shape":"Encryption", - "documentation":"

    " - }, + "Encryption":{"shape":"Encryption"}, "CannedACL":{ "shape":"ObjectCannedACL", "documentation":"

    The canned ACL to apply to the restore results.

    " @@ -7615,7 +7658,7 @@ "documentation":"

    The class of storage used to store the restore results.

    " } }, - "documentation":"

    Describes an S3 location that will receive the results of the restore request.

    " + "documentation":"

    Describes an Amazon S3 location that will receive the results of the restore request.

    " }, "SSECustomerAlgorithm":{"type":"string"}, "SSECustomerKey":{ @@ -7629,10 +7672,10 @@ "members":{ "KeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    Specifies the ID of the AWS Key Management Service (KMS) master encryption key to use for encrypting Inventory reports.

    " + "documentation":"

    Specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) to use for encrypting inventory reports.

    " } }, - "documentation":"

    Specifies the use of SSE-KMS to encrypt delivered Inventory reports.

    ", + "documentation":"

    Specifies the use of SSE-KMS to encrypt delivered inventory reports.

    ", "locationName":"SSE-KMS" }, "SSEKMSEncryptionContext":{ @@ -7647,7 +7690,7 @@ "type":"structure", "members":{ }, - "documentation":"

    Specifies the use of SSE-S3 to encrypt delivered Inventory reports.

    ", + "documentation":"

    Specifies the use of SSE-S3 to encrypt delivered inventory reports.

    ", "locationName":"SSE-S3" }, "ScanRange":{ @@ -7655,13 +7698,14 @@ "members":{ "Start":{ "shape":"Start", - "documentation":"

    Specifies the start of the byte range. This parameter is optional. Valid values: non-negative integers. The default value is 0.

    " + "documentation":"

    Specifies the start of the byte range. This parameter is optional. Valid values: non-negative integers. The default value is 0. If only start is supplied, it means scan from that point to the end of the file.For example; <scanrange><start>50</start></scanrange> means scan from byte 50 until the end of the file.

    " }, "End":{ "shape":"End", - "documentation":"

    Specifies the end of the byte range. This parameter is optional. Valid values: non-negative integers. The default value is one less than the size of the object being queried.

    " + "documentation":"

    Specifies the end of the byte range. This parameter is optional. Valid values: non-negative integers. The default value is one less than the size of the object being queried. If only the End parameter is supplied, it is interpreted to mean scan the last N bytes of the file. For example, <scanrange><end>50</end></scanrange> means scan the last 50 bytes.

    " } - } + }, + "documentation":"

    Specifies the byte range of the object to get the records from. A record is processed when its first byte is contained by the range. This parameter is optional, but when specified, it must not be empty. See RFC 2616, Section 14.35.1 about how to specify the start and end of the range.

    " }, "SelectObjectContentEventStream":{ "type":"structure", @@ -7687,7 +7731,7 @@ "documentation":"

    The End Event.

    " } }, - "documentation":"

    ", + "documentation":"

    The container for selecting objects from a content event stream.

    ", "eventstream":true }, "SelectObjectContentOutput":{ @@ -7695,7 +7739,7 @@ "members":{ "Payload":{ "shape":"SelectObjectContentEventStream", - "documentation":"

    " + "documentation":"

    The array of results.

    " } }, "payload":"Payload" @@ -7725,19 +7769,19 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    The SSE Algorithm used to encrypt the object. For more information, see Server-Side Encryption (Using Customer-Provided Encryption Keys.

    ", + "documentation":"

    The SSE Algorithm used to encrypt the object. For more information, see Server-Side Encryption (Using Customer-Provided Encryption Keys.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    The SSE Customer Key. For more information, see Server-Side Encryption (Using Customer-Provided Encryption Keys.

    ", + "documentation":"

    The SSE Customer Key. For more information, see Server-Side Encryption (Using Customer-Provided Encryption Keys.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    The SSE Customer Key MD5. For more information, see Server-Side Encryption (Using Customer-Provided Encryption Keys.

    ", + "documentation":"

    The SSE Customer Key MD5. For more information, see Server-Side Encryption (Using Customer-Provided Encryption Keys.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, @@ -7747,7 +7791,7 @@ }, "ExpressionType":{ "shape":"ExpressionType", - "documentation":"

    The type of the provided expression (for example., SQL).

    " + "documentation":"

    The type of the provided expression (for example, SQL).

    " }, "RequestProgress":{ "shape":"RequestProgress", @@ -7763,7 +7807,7 @@ }, "ScanRange":{ "shape":"ScanRange", - "documentation":"

    Specifies the byte range of the object to get the records from. A record is processed when its first byte is contained by the range. This parameter is optional, but when specified, it must not be empty. See RFC 2616, Section 14.35.1 about how to specify the start and end of the range.

    " + "documentation":"

    Specifies the byte range of the object to get the records from. A record is processed when its first byte is contained by the range. This parameter is optional, but when specified, it must not be empty. See RFC 2616, Section 14.35.1 about how to specify the start and end of the range.

    ScanRangemay be used in the following ways:

    • <scanrange><start>50</start><end>100</end></scanrange> - process only the records starting between the bytes 50 and 100 (inclusive, counting from zero)

    • <scanrange><start>50</start></scanrange> - process only the records starting after the byte 50

    • <scanrange><end>50</end></scanrange> - process only the records within the last 50 bytes of the file.

    " } }, "documentation":"

    Request to filter the contents of an Amazon S3 object based on a simple Structured Query Language (SQL) statement. In the request, along with the SQL expression, you must specify a data serialization format (JSON or CSV) of the object. Amazon S3 uses this to parse object data into records. It returns only records that match the specified SQL expression. You must also specify the data serialization format for the response. For more information, see S3Select API Documentation.

    " @@ -7783,7 +7827,7 @@ }, "ExpressionType":{ "shape":"ExpressionType", - "documentation":"

    The type of the provided expression (e.g., SQL).

    " + "documentation":"

    The type of the provided expression (for example, SQL).

    " }, "Expression":{ "shape":"Expression", @@ -7855,7 +7899,7 @@ "documentation":"

    A container for filter information for the selection of Amazon S3 objects encrypted with AWS KMS. If you include SourceSelectionCriteria in the replication configuration, this element is required.

    " } }, - "documentation":"

    A container that describes additional filters for identifying the source objects that you want to replicate. You can choose to enable or disable the replication of these objects. Currently, Amazon S3 supports only the filter that you can specify for objects created with server-side encryption using an AWS KMS-Managed Key (SSE-KMS).

    " + "documentation":"

    A container that describes additional filters for identifying the source objects that you want to replicate. You can choose to enable or disable the replication of these objects. Currently, Amazon S3 supports only the filter that you can specify for objects created with server-side encryption using a customer master key (CMK) stored in AWS Key Management Service (SSE-KMS).

    " }, "SseKmsEncryptedObjects":{ "type":"structure", @@ -7863,7 +7907,7 @@ "members":{ "Status":{ "shape":"SseKmsEncryptedObjectsStatus", - "documentation":"

    Specifies whether Amazon S3 replicates objects created with server-side encryption using an AWS KMS-managed key.

    " + "documentation":"

    Specifies whether Amazon S3 replicates objects created with server-side encryption using a customer master key (CMK) stored in AWS Key Management Service.

    " } }, "documentation":"

    A container for filter information for the selection of S3 objects encrypted with AWS KMS.

    " @@ -7893,7 +7937,7 @@ "documentation":"

    The total number of bytes of records payload data returned.

    " } }, - "documentation":"

    " + "documentation":"

    Container for the stats details.

    " }, "StatsEvent":{ "type":"structure", @@ -7904,7 +7948,7 @@ "eventpayload":true } }, - "documentation":"

    ", + "documentation":"

    Container for the Stats Event.

    ", "event":true }, "StorageClass":{ @@ -7945,7 +7989,7 @@ "documentation":"

    The place to store the data for an analysis.

    " } }, - "documentation":"

    " + "documentation":"

    Container for data related to the storage class analysis for an Amazon S3 bucket for export.

    " }, "StorageClassAnalysisSchemaVersion":{ "type":"string", @@ -7968,7 +8012,7 @@ "documentation":"

    Value of the tag.

    " } }, - "documentation":"

    " + "documentation":"

    A container of a key value name pair.

    " }, "TagCount":{"type":"integer"}, "TagSet":{ @@ -7984,10 +8028,10 @@ "members":{ "TagSet":{ "shape":"TagSet", - "documentation":"

    " + "documentation":"

    A collection for a set of tags

    " } }, - "documentation":"

    " + "documentation":"

    Container for TagSet elements.

    " }, "TaggingDirective":{ "type":"string", @@ -8003,14 +8047,14 @@ "members":{ "Grantee":{ "shape":"Grantee", - "documentation":"

    " + "documentation":"

    Container for the person being granted permissions.

    " }, "Permission":{ "shape":"BucketLogsPermission", "documentation":"

    Logging permissions assigned to the Grantee for the bucket.

    " } }, - "documentation":"

    " + "documentation":"

    Container for granting information.

    " }, "TargetGrants":{ "type":"list", @@ -8048,10 +8092,7 @@ "documentation":"

    The Amazon S3 bucket event about which to send notifications. For more information, see Supported Event Types in the Amazon Simple Storage Service Developer Guide.

    ", "locationName":"Event" }, - "Filter":{ - "shape":"NotificationConfigurationFilter", - "documentation":"

    " - } + "Filter":{"shape":"NotificationConfigurationFilter"} }, "documentation":"

    A container for specifying the configuration for publication of messages to an Amazon Simple Notification Service (Amazon SNS) topic when Amazon S3 detects specified events.

    " }, @@ -8061,7 +8102,7 @@ "Id":{"shape":"NotificationId"}, "Events":{ "shape":"EventList", - "documentation":"

    ", + "documentation":"

    A collection of events related to objects

    ", "locationName":"Event" }, "Event":{ @@ -8074,7 +8115,7 @@ "documentation":"

    Amazon SNS topic to which Amazon S3 will publish a message to report the specified events for the bucket.

    " } }, - "documentation":"

    " + "documentation":"

    A container for specifying the configuration for publication of messages to an Amazon Simple Notification Service (Amazon SNS) topic when Amazon S3 detects specified events. This data type is deprecated. Use TopicConfiguration instead.

    " }, "TopicConfigurationList":{ "type":"list", @@ -8135,11 +8176,11 @@ }, "CopyPartResult":{ "shape":"CopyPartResult", - "documentation":"

    " + "documentation":"

    Container for all response elements.

    " }, "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -8151,13 +8192,13 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) that was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -8181,7 +8222,7 @@ "members":{ "Bucket":{ "shape":"BucketName", - "documentation":"

    ", + "documentation":"

    The bucket name.

    ", "location":"uri", "locationName":"Bucket" }, @@ -8217,13 +8258,13 @@ }, "CopySourceRange":{ "shape":"CopySourceRange", - "documentation":"

    The range of bytes to copy from the source object. The range value must use the form bytes=first-last, where the first and last are the zero-based byte offsets to copy. For example, bytes=0-9 indicates that you want to copy the first ten bytes of the source. You can copy a range only if the source object is greater than 5 MB.

    ", + "documentation":"

    The range of bytes to copy from the source object. The range value must use the form bytes=first-last, where the first and last are the zero-based byte offsets to copy. For example, bytes=0-9 indicates that you want to copy the first 10 bytes of the source. You can copy a range only if the source object is greater than 5 MB.

    ", "location":"header", "locationName":"x-amz-copy-source-range" }, "Key":{ "shape":"ObjectKey", - "documentation":"

    ", + "documentation":"

    Object key for which the multipart upload was initiated.

    ", "location":"uri", "locationName":"Key" }, @@ -8241,25 +8282,25 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header. This must be the same encryption key specified in the initiate multipart upload request.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header. This must be the same encryption key specified in the initiate multipart upload request.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "CopySourceSSECustomerAlgorithm":{ "shape":"CopySourceSSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use when decrypting the source object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use when decrypting the source object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-copy-source-server-side-encryption-customer-algorithm" }, @@ -8271,7 +8312,7 @@ }, "CopySourceSSECustomerKeyMD5":{ "shape":"CopySourceSSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-copy-source-server-side-encryption-customer-key-MD5" }, @@ -8287,7 +8328,7 @@ "members":{ "ServerSideEncryption":{ "shape":"ServerSideEncryption", - "documentation":"

    The Server-side encryption algorithm used when storing this object in S3 (e.g., AES256, aws:kms).

    ", + "documentation":"

    The server-side encryption algorithm used when storing this object in Amazon S3 (for example, AES256, aws:kms).

    ", "location":"header", "locationName":"x-amz-server-side-encryption" }, @@ -8305,13 +8346,13 @@ }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round trip message integrity verification of the customer-provided encryption key.

    ", + "documentation":"

    If server-side encryption with a customer-provided encryption key was requested, the response will include this header to provide round-trip message integrity verification of the customer-provided encryption key.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, "SSEKMSKeyId":{ "shape":"SSEKMSKeyId", - "documentation":"

    If present, specifies the ID of the AWS Key Management Service (KMS) master encryption key that was used for the object.

    ", + "documentation":"

    If present, specifies the ID of the AWS Key Management Service (AWS KMS) symmetric customer managed customer master key (CMK) was used for the object.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-aws-kms-key-id" }, @@ -8350,7 +8391,7 @@ }, "ContentMD5":{ "shape":"ContentMD5", - "documentation":"

    The base64-encoded 128-bit MD5 digest of the part data. This parameter is auto-populated when using the command from the CLI. This parameted is required if object lock parameters are specified.

    ", + "documentation":"

    The base64-encoded 128-bit MD5 digest of the part data. This parameter is auto-populated when using the command from the CLI. This parameter is required if object lock parameters are specified.

    ", "location":"header", "locationName":"Content-MD5" }, @@ -8374,19 +8415,19 @@ }, "SSECustomerAlgorithm":{ "shape":"SSECustomerAlgorithm", - "documentation":"

    Specifies the algorithm to use to when encrypting the object (e.g., AES256).

    ", + "documentation":"

    Specifies the algorithm to use to when encrypting the object (for example, AES256).

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-algorithm" }, "SSECustomerKey":{ "shape":"SSECustomerKey", - "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header. This must be the same encryption key specified in the initiate multipart upload request.

    ", + "documentation":"

    Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This value is used to store the object and then it is discarded; Amazon S3 does not store the encryption key. The key must be appropriate for use with the algorithm specified in the x-amz-server-side​-encryption​-customer-algorithm header. This must be the same encryption key specified in the initiate multipart upload request.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key" }, "SSECustomerKeyMD5":{ "shape":"SSECustomerKeyMD5", - "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure the encryption key was transmitted without error.

    ", + "documentation":"

    Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses this header for a message integrity check to ensure that the encryption key was transmitted without error.

    ", "location":"header", "locationName":"x-amz-server-side-encryption-customer-key-MD5" }, diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/AclTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/AclTest.java index 0ca98e43462f..bc558e57fae7 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/AclTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/AclTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -18,9 +18,11 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.put; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static org.assertj.core.api.Assertions.assertThat; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.github.tomakehurst.wiremock.matching.ContainsPattern; @@ -33,6 +35,7 @@ import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.model.GetBucketAclResponse; import software.amazon.awssdk.services.s3.model.Grant; import software.amazon.awssdk.services.s3.model.Permission; import software.amazon.awssdk.services.s3.model.PutBucketAclRequest; @@ -71,6 +74,16 @@ public void putBucketAcl_marshalling() { verify(anyRequestedFor(anyUrl()).withRequestBody(new ContainsPattern(MOCK_ACL_RESPONSE))); } + @Test + public void getBucketAcl_shouldUnmarshallCorrectly() { + stubFor(get(anyUrl()) + .willReturn(aResponse().withBody(MOCK_ACL_RESPONSE).withStatus(200))); + + GetBucketAclResponse bucketAcl = s3Client.getBucketAcl(b -> b.bucket("test")); + assertThat(bucketAcl.owner()).isEqualTo(request().accessControlPolicy().owner()); + assertThat(bucketAcl.grants()).isEqualTo(request().accessControlPolicy().grants()); + } + private PutBucketAclRequest request() { List grants = new ArrayList<>(); diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/EndpointOverrideTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/EndpointOverrideTest.java index a552c0e32e56..0d58559d4e5d 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/EndpointOverrideTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/EndpointOverrideTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/MultipartUploadTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/MultipartUploadTest.java index 2ff1986e660d..1c314ded4fc5 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/MultipartUploadTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/MultipartUploadTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3MockUtils.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3MockUtils.java index fa0efc327a89..10e7d779e9bc 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3MockUtils.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3MockUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3PresignerTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3PresignerTest.java index 2fefca96a76c..5cf3f215c607 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3PresignerTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3PresignerTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3; import static org.assertj.core.api.Assertions.assertThat; @@ -15,11 +30,13 @@ import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.signer.AwsS3V4Signer; import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.signer.NoOpSigner; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.model.RequestPayer; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest; +import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest; @RunWith(MockitoJUnitRunner.class) public class S3PresignerTest { @@ -73,7 +90,7 @@ public void build_minimalProperties() { } @Test - public void basicGetObjectSignatureIsUrlCompatible() { + public void getObject_SignatureIsUrlCompatible() { PresignedGetObjectRequest presigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) .getObjectRequest(go -> go.bucket("foo34343434") @@ -85,7 +102,7 @@ public void basicGetObjectSignatureIsUrlCompatible() { } @Test - public void requesterPaysIsNotUrlCompatible() { + public void getObject_RequesterPaysIsNotUrlCompatible() { PresignedGetObjectRequest presigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) .getObjectRequest(go -> go.bucket("foo34343434") @@ -97,7 +114,7 @@ public void requesterPaysIsNotUrlCompatible() { } @Test - public void endpointOverrideIsIncludedInPresignedUrl() { + public void getObject_EndpointOverrideIsIncludedInPresignedUrl() { S3Presigner presigner = presignerBuilder().endpointOverride(URI.create("http://foo.com")).build(); PresignedGetObjectRequest presigned = presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) @@ -111,21 +128,7 @@ public void endpointOverrideIsIncludedInPresignedUrl() { } @Test - public void bodyAddedByInterceptorIsIncluded() { - S3Presigner presigner = presignerBuilder().endpointOverride(URI.create("http://foo.com")).build(); - PresignedGetObjectRequest presigned = - presigner.presignGetObject(r -> r.signatureDuration(Duration.ofMinutes(5)) - .getObjectRequest(go -> go.bucket("foo34343434") - .key("bar"))); - - assertThat(presigned.url().toString()).startsWith("http://foo.com/foo34343434/bar?"); - assertThat(presigned.isBrowserExecutable()).isTrue(); - assertThat(presigned.signedHeaders().get("host")).containsExactly("foo.com"); - assertThat(presigned.signedPayload()).isEmpty(); - } - - @Test - public void credentialsCanBeOverriddenAtTheRequestLevel() { + public void getObject_CredentialsCanBeOverriddenAtTheRequestLevel() { AwsCredentials clientCredentials = AwsBasicCredentials.create("a", "a"); AwsCredentials requestCredentials = AwsBasicCredentials.create("b", "b"); @@ -157,7 +160,7 @@ public void credentialsCanBeOverriddenAtTheRequestLevel() { } @Test - public void additionalHeadersAndQueryStringsCanBeAdded() { + public void getObject_AdditionalHeadersAndQueryStringsCanBeAdded() { AwsRequestOverrideConfiguration override = AwsRequestOverrideConfiguration.builder() .putHeader("X-Amz-AdditionalHeader", "foo1") @@ -178,7 +181,7 @@ public void additionalHeadersAndQueryStringsCanBeAdded() { } @Test - public void nonSigV4SignersRaisesException() { + public void getObject_NonSigV4SignersRaisesException() { AwsRequestOverrideConfiguration override = AwsRequestOverrideConfiguration.builder() .signer(new NoOpSigner()) @@ -193,7 +196,7 @@ public void nonSigV4SignersRaisesException() { } @Test - public void sigv4PresignerHonorsSignatureDuration() { + public void getObject_Sigv4PresignerHonorsSignatureDuration() { AwsRequestOverrideConfiguration override = AwsRequestOverrideConfiguration.builder() .signer(AwsS3V4Signer.create()) @@ -210,4 +213,116 @@ public void sigv4PresignerHonorsSignatureDuration() { assertThat(Integer.parseInt(expires)).isCloseTo(1234, Offset.offset(2)); }); } + + @Test + public void putObject_IsNotUrlCompatible() { + PresignedPutObjectRequest presigned = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(go -> go.bucket("foo34343434") + .key("bar"))); + assertThat(presigned.isBrowserExecutable()).isFalse(); + assertThat(presigned.signedHeaders().keySet()).containsExactlyInAnyOrder("host"); + assertThat(presigned.signedPayload()).isEmpty(); + } + + @Test + public void putObject_EndpointOverrideIsIncludedInPresignedUrl() { + S3Presigner presigner = presignerBuilder().endpointOverride(URI.create("http://foo.com")).build(); + PresignedPutObjectRequest presigned = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(go -> go.bucket("foo34343434") + .key("bar"))); + + assertThat(presigned.url().toString()).startsWith("http://foo.com/foo34343434/bar?"); + assertThat(presigned.isBrowserExecutable()).isFalse(); + assertThat(presigned.signedHeaders().get("host")).containsExactly("foo.com"); + assertThat(presigned.signedPayload()).isEmpty(); + } + + @Test + public void putObject_CredentialsCanBeOverriddenAtTheRequestLevel() { + AwsCredentials clientCredentials = AwsBasicCredentials.create("a", "a"); + AwsCredentials requestCredentials = AwsBasicCredentials.create("b", "b"); + + S3Presigner presigner = presignerBuilder().credentialsProvider(() -> clientCredentials).build(); + + + AwsRequestOverrideConfiguration overrideConfiguration = + AwsRequestOverrideConfiguration.builder() + .credentialsProvider(() -> requestCredentials) + .build(); + + PresignedPutObjectRequest presignedWithClientCredentials = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(go -> go.bucket("foo34343434") + .key("bar"))); + + PresignedPutObjectRequest presignedWithRequestCredentials = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(go -> go.bucket("foo34343434") + .key("bar") + .overrideConfiguration(overrideConfiguration))); + + System.out.println(presignedWithClientCredentials.url()); + + assertThat(presignedWithClientCredentials.httpRequest().rawQueryParameters().get("X-Amz-Credential").get(0)) + .startsWith("a"); + assertThat(presignedWithRequestCredentials.httpRequest().rawQueryParameters().get("X-Amz-Credential").get(0)) + .startsWith("b"); + } + + @Test + public void putObject_AdditionalHeadersAndQueryStringsCanBeAdded() { + AwsRequestOverrideConfiguration override = + AwsRequestOverrideConfiguration.builder() + .putHeader("X-Amz-AdditionalHeader", "foo1") + .putRawQueryParameter("additionalQueryParam", "foo2") + .build(); + + PresignedPutObjectRequest presigned = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(go -> go.bucket("foo34343434") + .key("bar") + .overrideConfiguration(override))); + + assertThat(presigned.isBrowserExecutable()).isFalse(); + assertThat(presigned.signedHeaders()).containsOnlyKeys("host", "x-amz-additionalheader"); + assertThat(presigned.signedHeaders().get("x-amz-additionalheader")).containsExactly("foo1"); + assertThat(presigned.httpRequest().headers()).containsKeys("x-amz-additionalheader"); + assertThat(presigned.httpRequest().rawQueryParameters().get("additionalQueryParam").get(0)).isEqualTo("foo2"); + } + + @Test + public void putObject_NonSigV4SignersRaisesException() { + AwsRequestOverrideConfiguration override = + AwsRequestOverrideConfiguration.builder() + .signer(new NoOpSigner()) + .build(); + + assertThatThrownBy(() -> presigner.presignPutObject(r -> r.signatureDuration(Duration.ofMinutes(5)) + .putObjectRequest(go -> go.bucket("foo34343434") + .key("bar") + .overrideConfiguration(override)))) + .isInstanceOf(IllegalStateException.class) + .hasMessageContaining("NoOpSigner"); + } + + @Test + public void putObject_Sigv4PresignerHonorsSignatureDuration() { + AwsRequestOverrideConfiguration override = + AwsRequestOverrideConfiguration.builder() + .signer(AwsS3V4Signer.create()) + .build(); + + PresignedPutObjectRequest presigned = + presigner.presignPutObject(r -> r.signatureDuration(Duration.ofSeconds(1234)) + .putObjectRequest(gor -> gor.bucket("a") + .key("b") + .overrideConfiguration(override))); + + assertThat(presigned.httpRequest().rawQueryParameters().get("X-Amz-Expires").get(0)).satisfies(expires -> { + assertThat(expires).containsOnlyDigits(); + assertThat(Integer.parseInt(expires)).isCloseTo(1234, Offset.offset(2)); + }); + } } \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3UtilitiesTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3UtilitiesTest.java index 93a90bcf708c..ea14ea6eaa3a 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3UtilitiesTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/S3UtilitiesTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -9,7 +9,7 @@ * * or in the "license" file accompanying this file. This file 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 governings3 + * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/bucketaddressingsep/VirtualHostAddressingSepTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/bucketaddressingsep/VirtualHostAddressingSepTest.java index 446427931f34..3451b53968d2 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/bucketaddressingsep/VirtualHostAddressingSepTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/bucketaddressingsep/VirtualHostAddressingSepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStreamTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStreamTest.java index f29d81c6e957..33b075be7142 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStreamTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumCalculatingInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumResetsOnRetryTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumResetsOnRetryTest.java new file mode 100644 index 000000000000..38c4ccd040b9 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumResetsOnRetryTest.java @@ -0,0 +1,144 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.checksums; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.core.async.AsyncResponseTransformer.toBytes; + +import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; +import com.github.tomakehurst.wiremock.client.WireMock; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import com.github.tomakehurst.wiremock.stubbing.Scenario; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.ResponseBytes; +import software.amazon.awssdk.core.async.AsyncRequestBody; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; +import software.amazon.awssdk.utils.BinaryUtils; + +/** + * Verifies that the checksum validators are reset on an HTTP retry. + */ +public class ChecksumResetsOnRetryTest { + @Rule + public WireMockRule mockServer = new WireMockRule(0); + + private S3Client s3Client; + + private S3AsyncClient s3AsyncClient; + + private byte[] body; + + private byte[] bodyWithTrailingChecksum; + + private String bodyEtag; + + @Before + public void setup() { + StaticCredentialsProvider credentials = StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")); + s3Client = S3Client.builder() + .credentialsProvider(credentials) + .region(Region.US_WEST_2) + .endpointOverride(URI.create("http://localhost:" + mockServer.port())) + .build(); + + s3AsyncClient = S3AsyncClient.builder() + .credentialsProvider(credentials) + .region(Region.US_WEST_2) + .endpointOverride(URI.create("http://localhost:" + mockServer.port())) + .build(); + + body = "foo".getBytes(StandardCharsets.UTF_8); + String checksumAsHexString = "acbd18db4cc2f85cedef654fccc4a4d8"; + bodyEtag = "\"" + checksumAsHexString + "\""; + bodyWithTrailingChecksum = ArrayUtils.addAll(body, BinaryUtils.fromHex(checksumAsHexString)); + } + + @Test + public void syncPutObject_resetsChecksumOnRetry() { + stubSuccessAfterOneRetry(r -> r.withHeader("ETag", bodyEtag)); + + PutObjectResponse response = s3Client.putObject(r -> r.bucket("foo").key("bar"), RequestBody.fromBytes(body)); + assertThat(response.eTag()).isEqualTo(bodyEtag); + } + + @Test + public void asyncPutObject_resetsChecksumOnRetry() { + stubSuccessAfterOneRetry(r -> r.withHeader("ETag", bodyEtag)); + + PutObjectResponse response = s3AsyncClient.putObject(r -> r.bucket("foo").key("bar"), AsyncRequestBody.fromBytes(body)).join(); + assertThat(response.eTag()).isEqualTo(bodyEtag); + } + + @Test + public void syncGetObject_resetsChecksumOnRetry() { + stubSuccessAfterOneRetry(r -> r.withHeader("ETag", bodyEtag) + .withHeader("x-amz-transfer-encoding", "append-md5") + .withHeader("content-length", Integer.toString(bodyWithTrailingChecksum.length)) + .withBody(bodyWithTrailingChecksum)); + + ResponseBytes response = s3Client.getObjectAsBytes(r -> r.bucket("foo").key("bar")); + assertThat(response.response().eTag()).isEqualTo(bodyEtag); + assertThat(response.asByteArray()).isEqualTo(body); + } + + @Test + public void asyncGetObject_resetsChecksumOnRetry() { + stubSuccessAfterOneRetry(r -> r.withHeader("ETag", bodyEtag) + .withHeader("x-amz-transfer-encoding", "append-md5") + .withHeader("content-length", Integer.toString(bodyWithTrailingChecksum.length)) + .withBody(bodyWithTrailingChecksum)); + + ResponseBytes response = s3AsyncClient.getObject(r -> r.bucket("foo").key("bar"), toBytes()).join(); + assertThat(response.response().eTag()).isEqualTo(bodyEtag); + assertThat(response.asByteArray()).isEqualTo(body); + } + + private void stubSuccessAfterOneRetry(Consumer successfulResponseModifier) { + WireMock.reset(); + + String scenario = "stubSuccessAfterOneRetry"; + stubFor(any(anyUrl()) + .willReturn(aResponse().withStatus(500).withBody("")) + .inScenario(scenario) + .whenScenarioStateIs(Scenario.STARTED) + .willSetStateTo("200")); + + ResponseDefinitionBuilder successfulResponse = aResponse().withStatus(200).withBody(""); + successfulResponseModifier.accept(successfulResponse); + stubFor(any(anyUrl()) + .willReturn(successfulResponse) + .inScenario(scenario) + .whenScenarioStateIs("200")); + } +} + diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisherTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisherTest.java index a89b547044b5..935b656d8539 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisherTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumValidatingPublisherTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidatorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidatorTest.java index ee951a62a367..b2e26e837ffa 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidatorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/checksums/ChecksumsEnabledValidatorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -25,13 +25,17 @@ import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.SERVER_SIDE_ENCRYPTION_HEADER; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.getObjectChecksumEnabledPerRequest; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.getObjectChecksumEnabledPerResponse; -import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.putObjectChecksumEnabled; +import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.responseChecksumIsValid; +import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.shouldRecordChecksum; import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AWS_KMS; import org.junit.Test; import software.amazon.awssdk.core.ClientType; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpFullResponse; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.model.GetObjectAclRequest; @@ -77,65 +81,86 @@ public void getObjectChecksumEnabledPerResponse_responseNotContainsChecksumHeade } @Test - public void putObjectChecksumEnabled_defaultTrue() { - assertThat(putObjectChecksumEnabled(PutObjectRequest.builder().build(), - ClientType.SYNC, - getSyncExecutionAttributes(), - SdkHttpFullResponse.builder().build())).isTrue(); + public void putObjectChecksumEnabled_defaultShouldRecord() { + assertThat(shouldRecordChecksum(PutObjectRequest.builder().build(), + ClientType.SYNC, + getSyncExecutionAttributes(), + emptyHttpRequest().build())).isTrue(); } @Test public void putObjectChecksumEnabled_nonPutObjectRequest_false() { - assertThat(putObjectChecksumEnabled(PutBucketAclRequest.builder().build(), - ClientType.SYNC, - getSyncExecutionAttributes(), - SdkHttpFullResponse.builder().build())).isFalse(); + assertThat(shouldRecordChecksum(PutBucketAclRequest.builder().build(), + ClientType.SYNC, + getSyncExecutionAttributes(), + emptyHttpRequest().build())).isFalse(); } @Test public void putObjectChecksumEnabled_disabledFromConfig_false() { ExecutionAttributes executionAttributes = getExecutionAttributesWithChecksumDisabled(); - assertThat(putObjectChecksumEnabled(PutObjectRequest.builder().build(), - ClientType.SYNC, - executionAttributes, - SdkHttpFullResponse.builder().build())).isFalse(); + assertThat(shouldRecordChecksum(PutObjectRequest.builder().build(), + ClientType.SYNC, + executionAttributes, + emptyHttpRequest().build())).isFalse(); } @Test public void putObjectChecksumEnabled_wrongClientType_false() { ExecutionAttributes executionAttributes = getSyncExecutionAttributes(); - assertThat(putObjectChecksumEnabled(PutObjectRequest.builder().build(), - ClientType.ASYNC, - executionAttributes, - SdkHttpFullResponse.builder().build())).isFalse(); + assertThat(shouldRecordChecksum(PutObjectRequest.builder().build(), + ClientType.ASYNC, + executionAttributes, + emptyHttpRequest().build())).isFalse(); } @Test public void putObjectChecksumEnabled_serverSideCustomerEncryption_false() { ExecutionAttributes executionAttributes = getSyncExecutionAttributes(); - SdkHttpFullResponse response = SdkHttpFullResponse.builder() - .putHeader(SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER, "test") - .build(); + SdkHttpRequest response = emptyHttpRequest().putHeader(SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER, "test") + .build(); - assertThat(putObjectChecksumEnabled(PutObjectRequest.builder().build(), - ClientType.SYNC, - executionAttributes, - response)).isFalse(); + assertThat(shouldRecordChecksum(PutObjectRequest.builder().build(), + ClientType.SYNC, + executionAttributes, + response)).isFalse(); } @Test public void putObjectChecksumEnabled_serverSideEncryption_false() { ExecutionAttributes executionAttributes = getSyncExecutionAttributes(); - SdkHttpFullResponse response = SdkHttpFullResponse.builder() - .putHeader(SERVER_SIDE_ENCRYPTION_HEADER, AWS_KMS.toString()) - .build(); + SdkHttpRequest response = emptyHttpRequest().putHeader(SERVER_SIDE_ENCRYPTION_HEADER, AWS_KMS.toString()) + .build(); - assertThat(putObjectChecksumEnabled(PutObjectRequest.builder().build(), - ClientType.SYNC, - executionAttributes, - response)).isFalse(); + assertThat(shouldRecordChecksum(PutObjectRequest.builder().build(), + ClientType.SYNC, + executionAttributes, + response)).isFalse(); + } + + @Test + public void responseChecksumIsValid_defaultTrue() { + assertThat(responseChecksumIsValid(SdkHttpResponse.builder().build())).isTrue(); + } + + @Test + public void responseChecksumIsValid_serverSideCustomerEncryption_false() { + SdkHttpResponse response = SdkHttpResponse.builder() + .putHeader(SERVER_SIDE_CUSTOMER_ENCRYPTION_HEADER, "test") + .build(); + + assertThat(responseChecksumIsValid(response)).isFalse(); + } + + @Test + public void responseChecksumIsValid_serverSideEncryption_false() { + SdkHttpResponse response = SdkHttpResponse.builder() + .putHeader(SERVER_SIDE_ENCRYPTION_HEADER, AWS_KMS.toString()) + .build(); + + assertThat(responseChecksumIsValid(response)).isFalse(); } private ExecutionAttributes getSyncExecutionAttributes() { @@ -157,4 +182,12 @@ private SdkHttpResponse getSdkHttpResponseWithChecksumHeader() { .build(); } + private SdkHttpRequest.Builder emptyHttpRequest() { + return SdkHttpFullRequest.builder() + .method(SdkHttpMethod.GET) + .protocol("https") + .host("localhost") + .port(80); + } + } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/functionaltests/CompleteMultipartUploadFunctionalTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/functionaltests/CompleteMultipartUploadFunctionalTest.java new file mode 100644 index 000000000000..c9488c9ca634 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/functionaltests/CompleteMultipartUploadFunctionalTest.java @@ -0,0 +1,305 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.functionaltests; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import java.util.concurrent.CompletionException; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import org.junit.Rule; +import org.junit.Test; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.S3Exception; + +public class CompleteMultipartUploadFunctionalTest { + private static final URI HTTP_LOCALHOST_URI = URI.create("http://localhost:8080/"); + + @Rule + public WireMockRule wireMock = new WireMockRule(); + + private S3ClientBuilder getSyncClientBuilder() { + + return S3Client.builder() + .region(Region.US_EAST_1) + .endpointOverride(HTTP_LOCALHOST_URI) + .credentialsProvider( + StaticCredentialsProvider.create(AwsBasicCredentials.create("key", "secret"))); + } + + private S3AsyncClientBuilder getAsyncClientBuilder() { + return S3AsyncClient.builder() + .region(Region.US_EAST_1) + .endpointOverride(HTTP_LOCALHOST_URI) + .credentialsProvider( + StaticCredentialsProvider.create(AwsBasicCredentials.create("key", "secret"))); + + } + + @Test + public void completeMultipartUpload_syncClient_completeResponse() { + String location = "http://Example-Bucket.s3.amazonaws.com/Example-Object"; + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String eTag = "\"3858f62230ac3c915f300c664312c11f-9\""; + String xmlResponseBody = String.format( + "\n" + + "\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "", location, bucket, key, eTag); + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3Client s3Client = getSyncClientBuilder().build(); + + CompleteMultipartUploadResponse response = s3Client.completeMultipartUpload( + r -> r.bucket(bucket).key(key).uploadId("upload-id")); + + assertThat(response.location()).isEqualTo(location); + assertThat(response.bucket()).isEqualTo(bucket); + assertThat(response.key()).isEqualTo(key); + assertThat(response.eTag()).isEqualTo(eTag); + } + + @Test + public void completeMultipartUpload_asyncClient_completeResponse() { + String location = "http://Example-Bucket.s3.amazonaws.com/Example-Object"; + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String eTag = "\"3858f62230ac3c915f300c664312c11f-9\""; + String xmlResponseBody = String.format( + "\n" + + "\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "%s\n" + + "", location, bucket, key, eTag); + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3AsyncClient s3Client = getAsyncClientBuilder().build(); + + CompleteMultipartUploadResponse response = s3Client.completeMultipartUpload( + r -> r.bucket(bucket).key(key).uploadId("upload-id")).join(); + + assertThat(response.location()).isEqualTo(location); + assertThat(response.bucket()).isEqualTo(bucket); + assertThat(response.key()).isEqualTo(key); + assertThat(response.eTag()).isEqualTo(eTag); + } + + @Test + public void completeMultipartUpload_syncClient_errorInResponseBody_correctType() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "InternalError\n" + + "We encountered an internal error. Please try again.\n" + + "656c76696e6727732072657175657374\n" + + "Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==\n" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3Client s3Client = getSyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id"))) + .isInstanceOf(S3Exception.class); + } + + @Test + public void completeMultipartUpload_asyncClient_errorInResponseBody_correctType() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "InternalError\n" + + "We encountered an internal error. Please try again.\n" + + "656c76696e6727732072657175657374\n" + + "Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==\n" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3AsyncClient s3Client = getAsyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id")) + .join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(S3Exception.class); + } + + @Test + public void completeMultipartUpload_syncClient_errorInResponseBody_correctCode() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "CustomError\n" + + "We encountered an internal error. Please try again.\n" + + "656c76696e6727732072657175657374\n" + + "Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==\n" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3Client s3Client = getSyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id"))) + .satisfies(e -> assertThat(((S3Exception)e).awsErrorDetails().errorCode()).isEqualTo("CustomError")); + } + + @Test + public void completeMultipartUpload_asyncClient_errorInResponseBody_correctCode() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "CustomError\n" + + "We encountered an internal error. Please try again.\n" + + "656c76696e6727732072657175657374\n" + + "Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==\n" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3AsyncClient s3Client = getAsyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id")) + .join()) + .satisfies(e -> { + S3Exception s3Exception = (S3Exception) e.getCause(); + assertThat(s3Exception.awsErrorDetails().errorCode()).isEqualTo("CustomError"); + }); + } + + @Test + public void completeMultipartUpload_syncClient_errorInResponseBody_correctMessage() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "CustomError\n" + + "Foo bar\n" + + "656c76696e6727732072657175657374\n" + + "Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==\n" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3Client s3Client = getSyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id"))) + .satisfies(e -> assertThat(((S3Exception)e).awsErrorDetails().errorMessage()).isEqualTo("Foo bar")); + } + + @Test + public void completeMultipartUpload_asyncClient_errorInResponseBody_correctMessage() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "CustomError\n" + + "Foo bar\n" + + "656c76696e6727732072657175657374\n" + + "Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==\n" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3AsyncClient s3Client = getAsyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id")) + .join()) + .satisfies(e -> { + S3Exception s3Exception = (S3Exception) e.getCause(); + assertThat(s3Exception.awsErrorDetails().errorMessage()).isEqualTo("Foo bar"); + }); + } + + @Test + public void completeMultipartUpload_syncClient_errorInResponseBody_invalidErrorXml() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3Client s3Client = getSyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id"))) + .isInstanceOf(S3Exception.class); + } + + @Test + public void completeMultipartUpload_asyncClient_errorInResponseBody_invalidErrorXml() { + String bucket = "Example-Bucket"; + String key = "Example-Object"; + String xmlResponseBody = "\n" + + "\n" + + "" + + ""; + + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(xmlResponseBody))); + + S3AsyncClient s3Client = getAsyncClientBuilder().build(); + + assertThatThrownBy(() -> s3Client.completeMultipartUpload(r -> r.bucket(bucket) + .key(key) + .uploadId("upload-id")) + .join()) + .isInstanceOf(CompletionException.class) + .hasCauseInstanceOf(S3Exception.class); + } +} diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/functionaltests/GetBucketPolicyFunctionalTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/functionaltests/GetBucketPolicyFunctionalTest.java new file mode 100644 index 000000000000..6933e8726648 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/functionaltests/GetBucketPolicyFunctionalTest.java @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.functionaltests; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.any; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.net.URI; +import java.util.concurrent.CompletionException; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; + +import org.junit.Rule; +import org.junit.Test; + +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.s3.model.CompleteMultipartUploadResponse; +import software.amazon.awssdk.services.s3.model.GetBucketPolicyResponse; +import software.amazon.awssdk.services.s3.model.S3Exception; + +public class GetBucketPolicyFunctionalTest { + private static final URI HTTP_LOCALHOST_URI = URI.create("http://localhost:8080/"); + private static final String EXAMPLE_BUCKET = "Example-Bucket"; + private static final String EXAMPLE_POLICY = + "{\"Version\":\"2012-10-17\",\"Id\":\"Policy1234\"," + + "\"Statement\":[{\"Sid\":\"Stmt1578431058575\",\"Effect\":\"Allow\"," + + "\"Principal\":{\"AWS\":\"arn:aws:iam::1234567890:root\"},\"Action\":\"s3:*\"," + + "\"Resource\":\"arn:aws:s3:::dummy-resource/*\"}]}"; + + @Rule + public WireMockRule wireMock = new WireMockRule(); + + private S3ClientBuilder getSyncClientBuilder() { + + return S3Client.builder() + .region(Region.US_EAST_1) + .endpointOverride(HTTP_LOCALHOST_URI) + .credentialsProvider( + StaticCredentialsProvider.create(AwsBasicCredentials.create("key", "secret"))); + } + + private S3AsyncClientBuilder getAsyncClientBuilder() { + return S3AsyncClient.builder() + .region(Region.US_EAST_1) + .endpointOverride(HTTP_LOCALHOST_URI) + .credentialsProvider( + StaticCredentialsProvider.create(AwsBasicCredentials.create("key", "secret"))); + + } + + @Test + public void getBucketPolicy_syncClient() { + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(EXAMPLE_POLICY))); + + S3Client s3Client = getSyncClientBuilder().build(); + + GetBucketPolicyResponse response = s3Client.getBucketPolicy(r -> r.bucket(EXAMPLE_BUCKET)); + assertThat(response.policy()).isEqualTo(EXAMPLE_POLICY); + } + + @Test + public void getBucketPolicy_asyncClient() { + stubFor(any(anyUrl()).willReturn(aResponse().withStatus(200).withBody(EXAMPLE_POLICY))); + + S3AsyncClient s3Client = getAsyncClientBuilder().build(); + + GetBucketPolicyResponse response = s3Client.getBucketPolicy(r -> r.bucket(EXAMPLE_BUCKET)).join(); + assertThat(response.policy()).isEqualTo(EXAMPLE_POLICY); + } +} diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptorTest.java index 97cc088242f2..b8376597ca9f 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/AsyncChecksumValidationInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ import software.amazon.awssdk.core.checksums.SdkChecksum; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.InterceptorContext; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpMethod; import software.amazon.awssdk.http.SdkHttpRequest; @@ -169,10 +170,18 @@ public void afterUnmarshalling_putObjectRequest_shouldValidateChecksum_throwExce .build(); Context.AfterUnmarshalling afterUnmarshallingContext = - InterceptorTestUtils.afterUnmarshallingContext(putObjectRequest, sdkHttpRequest, response, sdkHttpResponse); + InterceptorContext.builder() + .request(putObjectRequest) + .httpRequest(sdkHttpRequest) + .response(response) + .httpResponse(sdkHttpResponse) + .asyncRequestBody(AsyncRequestBody.fromString("Test")) + .build(); - assertThatThrownBy(() -> interceptor.afterUnmarshalling(afterUnmarshallingContext, getExecutionAttributesWithChecksum())) - .hasMessage("Data read has a different checksum than expected."); + ExecutionAttributes attributes = getExecutionAttributesWithChecksum(); + interceptor.modifyAsyncHttpContent(afterUnmarshallingContext, attributes); + assertThatThrownBy(() -> interceptor.afterUnmarshalling(afterUnmarshallingContext, attributes)) + .hasMessageContaining("Data read has a different checksum than expected."); } @Test diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptorTest.java index 99583344a225..cf9f5014e351 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateBucketInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptorTest.java index 13c034534fa4..586e62fa11e9 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/CreateMultipartUploadRequestInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptorTest.java index 8686ba0978d3..7f5a32dcae17 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/DecodeUrlEncodedResponseInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -31,12 +31,17 @@ import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; import software.amazon.awssdk.core.sync.RequestBody; -import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpFullResponse; import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.services.s3.model.CommonPrefix; import software.amazon.awssdk.services.s3.model.EncodingType; +import software.amazon.awssdk.services.s3.model.HeadObjectResponse; +import software.amazon.awssdk.services.s3.model.ListMultipartUploadsResponse; +import software.amazon.awssdk.services.s3.model.ListObjectVersionsResponse; import software.amazon.awssdk.services.s3.model.ListObjectsResponse; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.MultipartUpload; +import software.amazon.awssdk.services.s3.model.ObjectVersion; import software.amazon.awssdk.services.s3.model.S3Object; /** @@ -50,6 +55,13 @@ */ public class DecodeUrlEncodedResponseInterceptorTest { private static final String TEST_URL_ENCODED = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A"; + private static final String TEST_URL_ENCODED_DELIMITER = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A+delimiter"; + private static final String TEST_URL_ENCODED_NEXT_MARKER = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A+nextmarker"; + + private static final String TEST_URL_ENCODED_MARKER = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A+marker"; + private static final String TEST_URL_ENCODED_PREFIX = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A+prefix"; + private static final String TEST_URL_ENCODED_KEY = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A+key"; + private static final String TEST_URL_ENCODED_START_AFTER = "foo+%3D+bar+baz+%CE%B1+%CE%B2+%F0%9F%98%8A+startafter"; // foo = bar baz α β 😊 private static final String TEST_URL_DECODED = "foo = bar baz α β \uD83D\uDE0A"; @@ -62,22 +74,53 @@ public class DecodeUrlEncodedResponseInterceptorTest { S3Object.builder().key(TEST_URL_ENCODED).build() ); + private static final List COMMON_PREFIXES = Arrays.asList(CommonPrefix.builder() + .prefix(TEST_URL_ENCODED_PREFIX) + .build()); private static final ListObjectsResponse V1_TEST_ENCODED_RESPONSE = ListObjectsResponse.builder() - .encodingType(EncodingType.URL) - .delimiter(TEST_URL_ENCODED) - .nextMarker(TEST_URL_ENCODED) - .prefix(TEST_URL_ENCODED) - .marker(TEST_URL_ENCODED) - .contents(TEST_CONTENTS) - .build(); + .encodingType(EncodingType.URL) + .delimiter(TEST_URL_ENCODED_DELIMITER) + .nextMarker(TEST_URL_ENCODED_NEXT_MARKER) + .prefix(TEST_URL_ENCODED_PREFIX) + .marker(TEST_URL_ENCODED_MARKER) + .contents(TEST_CONTENTS) + .commonPrefixes(COMMON_PREFIXES) + .build(); private static final ListObjectsV2Response V2_TEST_ENCODED_RESPONSE = ListObjectsV2Response.builder() - .encodingType(EncodingType.URL) - .delimiter(TEST_URL_ENCODED) - .prefix(TEST_URL_ENCODED) - .startAfter(TEST_URL_ENCODED) - .contents(TEST_CONTENTS) - .build(); + .encodingType(EncodingType.URL) + .delimiter(TEST_URL_ENCODED_DELIMITER) + .prefix(TEST_URL_ENCODED_PREFIX) + .startAfter(TEST_URL_ENCODED_START_AFTER) + .contents(TEST_CONTENTS) + .commonPrefixes(COMMON_PREFIXES) + .build(); + + private static final String TEST_URL_ENCODED_NEXT_KEY_MARKER = TEST_URL_ENCODED + "+nextKeyMarker"; + private static final String TEST_URL_ENCODED_KEY_MARKER = TEST_URL_ENCODED + "+keyMarker"; + private static final ListObjectVersionsResponse TEST_LIST_OBJECT_VERSION_RESPONSE = ListObjectVersionsResponse.builder() + .encodingType(EncodingType.URL) + .delimiter(TEST_URL_ENCODED_DELIMITER) + .prefix(TEST_URL_ENCODED_PREFIX) + .keyMarker(TEST_URL_ENCODED_KEY_MARKER) + .nextKeyMarker(TEST_URL_ENCODED_NEXT_KEY_MARKER) + .commonPrefixes(COMMON_PREFIXES) + .versions(ObjectVersion.builder() + .key(TEST_URL_ENCODED_KEY) + .build()) + .build(); + + + private static final ListMultipartUploadsResponse TEST_LIST_MULTIPART_UPLOADS_RESPONSE = + ListMultipartUploadsResponse.builder() + .encodingType(EncodingType.URL) + .delimiter(TEST_URL_ENCODED_DELIMITER) + .prefix(TEST_URL_ENCODED_PREFIX) + .keyMarker(TEST_URL_ENCODED_KEY_MARKER) + .nextKeyMarker(TEST_URL_ENCODED_NEXT_KEY_MARKER) + .uploads(MultipartUpload.builder().key(TEST_URL_ENCODED_KEY).build()) + .commonPrefixes(COMMON_PREFIXES) + .build(); @Test public void encodingTypeSet_decodesListObjectsResponseParts() { @@ -85,11 +128,12 @@ public void encodingTypeSet_decodesListObjectsResponseParts() { ListObjectsResponse decoded = (ListObjectsResponse) INTERCEPTOR.modifyResponse(ctx, new ExecutionAttributes()); - assertDecoded(decoded::delimiter); - assertDecoded(decoded::nextMarker); - assertDecoded(decoded::prefix); - assertDecoded(decoded::marker); + assertDecoded(decoded::delimiter, " delimiter"); + assertDecoded(decoded::nextMarker, " nextmarker"); + assertDecoded(decoded::prefix, " prefix"); + assertDecoded(decoded::marker, " marker"); assertKeysAreDecoded(decoded.contents()); + assertCommonPrefixesAreDecoded(decoded.commonPrefixes()); } @Test @@ -98,10 +142,40 @@ public void encodingTypeSet_decodesListObjectsV2ResponseParts() { ListObjectsV2Response decoded = (ListObjectsV2Response) INTERCEPTOR.modifyResponse(ctx, new ExecutionAttributes()); - assertDecoded(decoded::delimiter); - assertDecoded(decoded::prefix); - assertDecoded(decoded::startAfter); + assertDecoded(decoded::delimiter, " delimiter"); + assertDecoded(decoded::prefix, " prefix"); + assertDecoded(decoded::startAfter, " startafter"); assertKeysAreDecoded(decoded.contents()); + assertCommonPrefixesAreDecoded(decoded.commonPrefixes()); + } + + @Test + public void encodingTypeSet_decodesListObjectVersionsResponse() { + Context.ModifyResponse ctx = newContext(TEST_LIST_OBJECT_VERSION_RESPONSE); + + ListObjectVersionsResponse decoded = (ListObjectVersionsResponse) INTERCEPTOR.modifyResponse(ctx, new ExecutionAttributes()); + + assertDecoded(decoded::delimiter, " delimiter"); + assertDecoded(decoded::prefix, " prefix"); + assertDecoded(decoded::keyMarker, " keyMarker"); + assertDecoded(decoded::nextKeyMarker, " nextKeyMarker"); + assertCommonPrefixesAreDecoded(decoded.commonPrefixes()); + assertVersionsAreDecoded(decoded.versions()); + } + + @Test + public void encodingTypeSet_decodesListMultipartUploadsResponse() { + Context.ModifyResponse ctx = newContext(TEST_LIST_MULTIPART_UPLOADS_RESPONSE); + + ListMultipartUploadsResponse decoded = (ListMultipartUploadsResponse) INTERCEPTOR.modifyResponse(ctx, new ExecutionAttributes()); + + assertDecoded(decoded::delimiter, " delimiter"); + assertDecoded(decoded::prefix, " prefix"); + assertDecoded(decoded::keyMarker, " keyMarker"); + assertDecoded(decoded::nextKeyMarker, " nextKeyMarker"); + assertCommonPrefixesAreDecoded(decoded.commonPrefixes()); + assertUploadsAreDecoded(decoded.uploads()); + assertCommonPrefixesAreDecoded(decoded.commonPrefixes()); } @Test @@ -130,14 +204,39 @@ public void encodingTypeNotSet_doesNotDecodeListObjectsV2ResponseParts() { assertThat(fromInterceptor).isEqualTo(original); } + @Test + public void otherResponses_shouldNotModifyResponse() { + HeadObjectResponse original = HeadObjectResponse.builder().build(); + Context.ModifyResponse ctx = newContext(original); + SdkResponse sdkResponse = INTERCEPTOR.modifyResponse(ctx, new ExecutionAttributes()); + assertThat(original.hashCode()).isEqualTo(sdkResponse.hashCode()); + } + private void assertKeysAreDecoded(List objects) { objects.forEach(o -> assertDecoded(o::key)); } + private void assertCommonPrefixesAreDecoded(List commonPrefixes) { + commonPrefixes.forEach(c -> assertDecoded(c::prefix, " prefix")); + } + private void assertDecoded(Supplier supplier) { - assertThat(supplier.get()).isEqualTo(TEST_URL_DECODED); + assertDecoded(supplier, ""); + } + + private void assertDecoded(Supplier supplier, String suffix) { + assertThat(supplier.get()).isEqualTo(TEST_URL_DECODED + suffix); } + private void assertVersionsAreDecoded(List versions) { + versions.forEach(v -> assertDecoded(v::key, " key")); + } + + private void assertUploadsAreDecoded(List uploads) { + uploads.forEach(u -> assertDecoded(u::key, " key")); + } + + private static Context.ModifyResponse newContext(SdkResponse response) { return new Context.ModifyResponse() { @Override diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptorTest.java index f3230392287f..4f03921cbb69 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableChunkedEncodingInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptorTest.java index c3ed27d0fe6f..400e20761b6f 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EnableTrailingChecksumInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptorTest.java index a41382f9c0b1..73f63be12fe9 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/EndpointAddressInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -16,12 +16,17 @@ package software.amazon.awssdk.services.s3.internal.handlers; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute.SIGNING_REGION; import static software.amazon.awssdk.awscore.AwsExecutionAttribute.AWS_REGION; import static software.amazon.awssdk.core.interceptor.SdkExecutionAttribute.SERVICE_CONFIG; +import static software.amazon.awssdk.utils.http.SdkHttpUtils.urlEncode; import java.net.URI; import java.util.Optional; import org.junit.Test; + +import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.async.AsyncRequestBody; import software.amazon.awssdk.core.interceptor.Context; @@ -112,12 +117,263 @@ public void pathStyleAccessEnabled_shouldNotConvertToDnsEndpoint() { S3Configuration.builder().pathStyleAccessEnabled(true)); } - private void verifyVirtualStyleConvertDnsEndpoint(String protocol) { - URI customUri = URI.create(String.format("%s://s3-test.com", protocol)); - String bucketName = "some-bucket"; - URI expectedUri = URI.create(String.format("%s://%s.s3.dualstack.us-east-1.amazonaws.com", protocol, bucketName)); + @Test + public void accesspointArn_shouldConvertEndpoint() { + verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + S3Configuration.builder()); + verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint:foobar", + "https://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + S3Configuration.builder()); + } + + @Test + public void accesspointArn_futureUnknownRegion_US_correctlyInfersPartition() { + verifyAccesspointArn("http", + "arn:aws:s3:us-future-1:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.us-future-1.amazonaws.com", + Region.of("us-future-1"), + S3Configuration.builder(), + Region.of("us-future-1")); + } + + @Test + public void accesspointArn_futureUnknownRegion_crossRegion_correctlyInfersPartition() { + verifyAccesspointArn("http", + "arn:aws:s3:us-future-2:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.us-future-2.amazonaws.com", + Region.of("us-future-2"), + S3Configuration.builder().useArnRegionEnabled(true), + Region.of("us-future-1")); + } + + @Test + public void accesspointArn_futureUnknownRegion_CN_correctlyInfersPartition() { + verifyAccesspointArn("http", + "arn:aws-cn:s3:cn-future-1:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.cn-future-1.amazonaws.com.cn", + Region.of("cn-future-1"), + S3Configuration.builder(), + Region.of("cn-future-1")); + } + + @Test + public void accesspointArn_futureUnknownRegionAndPartition_defaultsToAws() { + verifyAccesspointArn("http", + "arn:aws:s3:unknown:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.unknown.amazonaws.com", + Region.of("unknown"), + S3Configuration.builder(), + Region.of("unknown")); + } + + @Test + public void malformedArn_throwsIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:foobar", + null, + S3Configuration.builder())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ARN"); + } + + @Test + public void unsupportedArn_throwsIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:unsupported:foobar", + null, + S3Configuration.builder())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("ARN"); + } + + @Test + public void accesspointArn_invalidPartition_throwsIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:bar:s3:us-east-1:12345678910:accesspoint:foobar", + null, + S3Configuration.builder())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("bar"); + } + + @Test + public void bucketArn_throwsIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:bucket_name:foobar", + null, + S3Configuration.builder())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("bucket parameter"); + } + + + @Test + public void accesspointArn_withSlashes_shouldConvertEndpoint() { + verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + S3Configuration.builder()); + verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "https://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + S3Configuration.builder()); + } + + @Test + public void accesspointArn_withDualStackEnabled_shouldConvertEndpoint() { + verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.dualstack.us-east-1.amazonaws.com", + S3Configuration.builder().dualstackEnabled(true)); + verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "https://foobar-12345678910.s3-accesspoint.dualstack.us-east-1.amazonaws.com", + S3Configuration.builder().dualstackEnabled(true)); + } + + @Test + public void accesspointArn_withCnPartition_shouldConvertEndpoint() { + verifyAccesspointArn("http", + "arn:aws-cn:s3:cn-north-1:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.cn-north-1.amazonaws.com.cn", + Region.of("cn-north-1"), + S3Configuration.builder(), + Region.of("cn-north-1")); + verifyAccesspointArn("https", + "arn:aws-cn:s3:cn-north-1:12345678910:accesspoint:foobar", + "https://foobar-12345678910.s3-accesspoint.cn-north-1.amazonaws.com.cn", + Region.of("cn-north-1"), + S3Configuration.builder(), + Region.of("cn-north-1")); + } + + @Test + public void accesspointArn_withDifferentPartition_useArnRegionEnabled_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws-cn:s3:cn-north-1:12345678910:accesspoint:foobar", + "http://foobar-12345678910.s3-accesspoint.cn-north-1.amazonaws.com.cn", + Region.of("cn-north-1"), + S3Configuration.builder().useArnRegionEnabled(true), + Region.of("us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("partition"); + } + + @Test + public void accesspointArn_withFipsRegionPrefix_useArnRegionEnabled_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder().useArnRegionEnabled(true), + Region.of("fips-us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + assertThatThrownBy(() -> verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "https://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder().useArnRegionEnabled(true), + Region.of("fips-us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + } + + @Test + public void accesspointArn_withFipsRegionPrefix_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder(), + Region.of("fips-us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + assertThatThrownBy(() -> verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "https://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder(), + Region.of("fips-us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + } + + @Test + public void accesspointArn_withFipsRegionSuffix_useArnRegionEnabled_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder().useArnRegionEnabled(true), + Region.of("us-east-1-fips"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + assertThatThrownBy(() -> verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "https://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder().useArnRegionEnabled(true), + Region.of("us-east-1-fips"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + } + + @Test + public void accesspointArn_withFipsRegionSuffix_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder(), + Region.of("us-east-1-fips"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + assertThatThrownBy(() -> verifyAccesspointArn("https", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "https://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder(), + Region.of("us-east-1-fips"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("FIPS"); + } + + @Test + public void accesspointArn_withAccelerateEnabled_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder().accelerateModeEnabled(true), + Region.of("us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accelerate"); + } + @Test + public void accesspointArn_withPathStyleAddressingEnabled_shouldThrowIllegalArgumentException() { + assertThatThrownBy(() -> verifyAccesspointArn("http", + "arn:aws:s3:us-east-1:12345678910:accesspoint/foobar", + "http://foobar-12345678910.s3-accesspoint.us-east-1.amazonaws.com", + Region.of("us-east-1"), + S3Configuration.builder().pathStyleAccessEnabled(true), + Region.of("us-east-1"))) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("path style"); + } + + private void verifyVirtualStyleConvertDnsEndpoint(String protocol) { + String bucketName = "test-bucket"; + String key = "test-key"; + URI customUri = URI.create(String.format("%s://s3-test.com/%s/%s", protocol, bucketName, key)); + URI expectedUri = URI.create(String.format("%s://%s.s3.dualstack.us-east-1.amazonaws.com/%s", protocol, + bucketName, key)); + Context.ModifyHttpRequest ctx = context(ListObjectsV2Request.builder().bucket(bucketName).build(), sdkHttpRequest(customUri)); ExecutionAttributes executionAttributes = new ExecutionAttributes(); @@ -137,6 +393,7 @@ private SdkHttpRequest sdkHttpRequest(URI customUri) { .host(customUri.getHost()) .port(customUri.getPort()) .method(SdkHttpMethod.GET) + .encodedPath(customUri.getPath()) .build(); } @@ -156,8 +413,10 @@ private void verifyAccelerateDisabledOperationsEndpointNotConverted(SdkRequest r private void verifyEndpoint(String protocol, String expectedEndpoint, S3Configuration.Builder builder) { - URI customUri = URI.create(String.format("%s://s3-test.com", protocol)); - URI expectedUri = URI.create(expectedEndpoint); + String bucket = "test-bucket"; + String key = "test-key"; + URI customUri = URI.create(String.format("%s://s3-test.com/%s/%s", protocol, bucket, key)); + URI expectedUri = URI.create(String.format("%s/%s/%s", expectedEndpoint, bucket, key)); Context.ModifyHttpRequest ctx = context(PutObjectRequest.builder().build(), sdkHttpRequest(customUri)); ExecutionAttributes executionAttributes = new ExecutionAttributes(); S3Configuration s3Configuration = builder.build(); @@ -170,6 +429,38 @@ private void verifyEndpoint(String protocol, String expectedEndpoint, assertThat(sdkHttpFullRequest.getUri()).isEqualTo(expectedUri); } + private void verifyAccesspointArn(String protocol, String accessPointArn, String expectedEndpoint, + Region expectedSigningRegion, + S3Configuration.Builder builder, Region region) { + String key = "test-key"; + + URI customUri = URI.create(String.format("%s://s3-test.com/%s/%s", protocol, urlEncode(accessPointArn), key)); + URI expectedUri = URI.create(String.format("%s/%s", expectedEndpoint, key)); + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(accessPointArn) + .key(key) + .build(); + Context.ModifyHttpRequest ctx = context(putObjectRequest, sdkHttpRequest(customUri)); + ExecutionAttributes executionAttributes = new ExecutionAttributes(); + S3Configuration s3Configuration = builder.build(); + + executionAttributes.putAttribute(SERVICE_CONFIG, s3Configuration); + executionAttributes.putAttribute(AWS_REGION, region); + executionAttributes.putAttribute(SIGNING_REGION, region); + + SdkHttpRequest sdkHttpFullRequest = interceptor.modifyHttpRequest(ctx, executionAttributes); + + assertThat(executionAttributes.getAttribute(SIGNING_REGION)) + .isEqualTo(expectedSigningRegion); + assertThat(sdkHttpFullRequest.getUri()).isEqualTo(expectedUri); + } + + + private void verifyAccesspointArn(String protocol, String accessPointArn, String expectedEndpoint, + S3Configuration.Builder builder) { + verifyAccesspointArn(protocol, accessPointArn, expectedEndpoint, Region.US_EAST_1, builder, Region.US_EAST_1); + } + private Context.ModifyHttpRequest context(SdkRequest request, SdkHttpRequest sdkHttpRequest) { return new Context.ModifyHttpRequest() { @Override diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptorTest.java index 65dfad573661..b93b6b26851c 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/ExceptionTranslationInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptorTest.java index bc6d20800e77..95ee7178e58f 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/GetBucketPolicyInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectHeaderTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectHeaderTest.java index e03ff18547f8..0203649a477c 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectHeaderTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectHeaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptorTest.java index db120eb29bcc..13bc671086b1 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/PutObjectInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptorTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptorTest.java index 8a428d53e4af..90b779cdfd25 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptorTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/handlers/SyncChecksumValidationInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,17 +17,22 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Java6Assertions.assertThatThrownBy; import static software.amazon.awssdk.core.ClientType.SYNC; import static software.amazon.awssdk.core.interceptor.SdkExecutionAttribute.CLIENT_TYPE; import static software.amazon.awssdk.core.interceptor.SdkExecutionAttribute.SERVICE_CONFIG; import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.CHECKSUM_ENABLED_RESPONSE_HEADER; import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.CONTENT_LENGTH_HEADER; import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.ENABLE_MD5_CHECKSUM_HEADER_VALUE; +import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.SERVER_SIDE_ENCRYPTION_HEADER; import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.CHECKSUM; +import static software.amazon.awssdk.services.s3.model.ServerSideEncryption.AWS_KMS; import java.io.IOException; import java.io.InputStream; +import java.net.URI; import java.nio.ByteBuffer; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -36,8 +41,12 @@ import software.amazon.awssdk.core.checksums.SdkChecksum; import software.amazon.awssdk.core.interceptor.Context; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.InterceptorContext; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.http.ContentStreamProvider; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.SdkHttpMethod; +import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.checksums.ChecksumCalculatingInputStream; @@ -45,12 +54,18 @@ import software.amazon.awssdk.services.s3.internal.handlers.SyncChecksumValidationInterceptor.ChecksumCalculatingStreamProvider; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; import software.amazon.awssdk.services.s3.utils.InterceptorTestUtils; import software.amazon.awssdk.utils.IoUtils; import software.amazon.awssdk.utils.StringInputStream; +import software.amazon.awssdk.utils.internal.Base16Lower; public class SyncChecksumValidationInterceptorTest { + private static final byte[] CONTENT_BYTES = "CONTENT".getBytes(Charset.forName("UTF-8")); + private static final String VALID_CHECKSUM = Base16Lower.encodeAsString(checkSumFor(CONTENT_BYTES).getChecksumBytes()); + private static final String INVALID_CHECKSUM = "3902ee7e149eb8313a34757e89e21af6"; + private SyncChecksumValidationInterceptor interceptor = new SyncChecksumValidationInterceptor(); @Test @@ -149,6 +164,82 @@ public void checksumCalculatingStreamProvider_shouldReturnNewStreamResetChecksum newStream.close(); } + @Test + public void afterUnmarshalling_putObjectRequest_shouldValidateChecksum() { + SdkHttpResponse sdkHttpResponse = getSdkHttpResponseWithChecksumHeader(); + + PutObjectResponse response = PutObjectResponse.builder() + .eTag(VALID_CHECKSUM) + .build(); + + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .build(); + + SdkHttpRequest sdkHttpRequest = SdkHttpFullRequest.builder() + .uri(URI.create("http://localhost:8080")) + .method(SdkHttpMethod.PUT) + .build(); + + Context.AfterUnmarshalling afterUnmarshallingContext = + InterceptorTestUtils.afterUnmarshallingContext(putObjectRequest, sdkHttpRequest, response, sdkHttpResponse); + + interceptor.afterUnmarshalling(afterUnmarshallingContext, getExecutionAttributesWithChecksum()); + } + + @Test + public void afterUnmarshalling_putObjectRequest_shouldValidateChecksum_throwExceptionIfInvalid() { + SdkHttpResponse sdkHttpResponse = getSdkHttpResponseWithChecksumHeader(); + + PutObjectResponse response = PutObjectResponse.builder() + .eTag(INVALID_CHECKSUM) + .build(); + + PutObjectRequest putObjectRequest = PutObjectRequest.builder().build(); + + SdkHttpRequest sdkHttpRequest = SdkHttpFullRequest.builder() + .uri(URI.create("http://localhost:8080")) + .method(SdkHttpMethod.PUT) + .contentStreamProvider(() -> new StringInputStream("Test")) + .build(); + + Context.AfterUnmarshalling afterUnmarshallingContext = + InterceptorContext.builder() + .request(putObjectRequest) + .httpRequest(sdkHttpRequest) + .response(response) + .httpResponse(sdkHttpResponse) + .requestBody(RequestBody.fromString("Test")) + .build(); + + ExecutionAttributes attributes = getExecutionAttributesWithChecksum(); + interceptor.modifyHttpContent(afterUnmarshallingContext, attributes); + assertThatThrownBy(() -> interceptor.afterUnmarshalling(afterUnmarshallingContext, attributes)) + .hasMessageContaining("Data read has a different checksum than expected."); + } + + @Test + public void afterUnmarshalling_putObjectRequest_with_SSE_shouldNotValidateChecksum() { + SdkHttpResponse sdkHttpResponse = getSdkHttpResponseWithChecksumHeader(); + + PutObjectResponse response = PutObjectResponse.builder() + .eTag(INVALID_CHECKSUM) + .build(); + + PutObjectRequest putObjectRequest = PutObjectRequest.builder().build(); + + SdkHttpRequest sdkHttpRequest = SdkHttpFullRequest.builder() + .putHeader(SERVER_SIDE_ENCRYPTION_HEADER, AWS_KMS.toString()) + .putHeader("x-amz-server-side-encryption-aws-kms-key-id", ENABLE_MD5_CHECKSUM_HEADER_VALUE) + .uri(URI.create("http://localhost:8080")) + .method(SdkHttpMethod.PUT) + .build(); + + Context.AfterUnmarshalling afterUnmarshallingContext = + InterceptorTestUtils.afterUnmarshallingContext(putObjectRequest, sdkHttpRequest, response, sdkHttpResponse); + + interceptor.afterUnmarshalling(afterUnmarshallingContext, getExecutionAttributesWithChecksum()); + } + private static final class CloseAwareStream extends InputStream { private StringInputStream inputStream; private boolean isClosed; @@ -192,4 +283,15 @@ private ExecutionAttributes getExecutionAttributesWithChecksumDisabled() { executionAttributes.putAttribute(SERVICE_CONFIG, S3Configuration.builder().checksumValidationEnabled(false).build()); return executionAttributes; } + + private ExecutionAttributes getExecutionAttributesWithChecksum() { + SdkChecksum checksum = checkSumFor(CONTENT_BYTES); + return getExecutionAttributes().putAttribute(CHECKSUM, checksum); + } + + private static SdkChecksum checkSumFor(byte[] bytes) { + SdkChecksum checksum = new Md5Checksum(); + checksum.update(bytes, 0, bytes.length); + return checksum; + } } diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointBuilderTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointBuilderTest.java new file mode 100644 index 000000000000..781faf7c89dc --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointBuilderTest.java @@ -0,0 +1,173 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; + +import java.net.URI; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class S3AccessPointBuilderTest { + private static final String LONG_STRING_64 = "1234567890123456789012345678901234567890123456789012345678901234"; + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void toURI_noDualstack() { + URI result = S3AccessPointBuilder.create() + .accessPointName("access-point") + .accountId("account-id") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri(); + + assertThat(result, is(URI.create("protocol://access-point-account-id.s3-accesspoint.region.domain"))); + } + + @Test + public void toURI_dualstack() { + URI result = S3AccessPointBuilder.create() + .accessPointName("access-point") + .accountId("account-id") + .region("region") + .protocol("protocol") + .domain("domain") + .dualstackEnabled(true) + .toUri(); + + assertThat(result, + is(URI.create("protocol://access-point-account-id.s3-accesspoint.dualstack.region.domain"))); + } + + @Test + public void toURI_accessPointNameWithSlashes_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("access/point") + .accountId("account-id") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accessPointName") + .hasMessageContaining("alphanumeric"); + } + + @Test + public void toURI_accountIdWithSlashes_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("accesspoint") + .accountId("account/id") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accountId") + .hasMessageContaining("alphanumeric"); + } + + @Test + public void toURI_accessPointNameWithTooLongString_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName(LONG_STRING_64) + .accountId("account-id") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accessPointName") + .hasMessageContaining("63"); // max length + } + + @Test + public void toURI_accountIdWithTooLongString_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("accesspoint") + .accountId(LONG_STRING_64) + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accountId") + .hasMessageContaining("63"); // max length + } + + @Test + public void toURI_accessPointNameWithEmptyString_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("") + .accountId("account-id") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accessPointName") + .hasMessageContaining("missing"); + } + + @Test + public void toURI_accountIdWithEmptyString_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("accesspoint") + .accountId("") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accountId") + .hasMessageContaining("missing"); + } + + @Test + public void toURI_accessPointNameWithUrlEncodedCharacters_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("access%2fpoint") + .accountId("account-id") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accessPointName") + .hasMessageContaining("alphanumeric"); + } + + @Test + public void toURI_accountIdWithUrlEncodedCharacters_throwsIllegalArgumentException() { + assertThatThrownBy(() -> S3AccessPointBuilder.create() + .accessPointName("accesspoint") + .accountId("account%2fid") + .region("region") + .protocol("protocol") + .domain("domain") + .toUri()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("accountId") + .hasMessageContaining("alphanumeric"); + } +} \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointResourceTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointResourceTest.java new file mode 100644 index 000000000000..adf5dd1b3b39 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3AccessPointResourceTest.java @@ -0,0 +1,205 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import java.util.Optional; + +import org.junit.Test; + +public class S3AccessPointResourceTest { + @Test + public void buildWithAllPropertiesSet() { + S3AccessPointResource s3AccessPointResource = S3AccessPointResource.builder() + .accessPointName("access_point-name") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + assertEquals("access_point-name", s3AccessPointResource.accessPointName()); + assertEquals(Optional.of("account-id"), s3AccessPointResource.accountId()); + assertEquals(Optional.of("partition"), s3AccessPointResource.partition()); + assertEquals(Optional.of("region"), s3AccessPointResource.region()); + assertEquals("accesspoint", s3AccessPointResource.type()); + } + + @Test + public void toBuilder() { + S3AccessPointResource s3AccessPointResource = S3AccessPointResource.builder() + .accessPointName("access_point-name") + .accountId("account-id") + .partition("partition") + .region("region") + .build() + .toBuilder() + .build(); + + assertEquals("access_point-name", s3AccessPointResource.accessPointName()); + assertEquals(Optional.of("account-id"), s3AccessPointResource.accountId()); + assertEquals(Optional.of("partition"), s3AccessPointResource.partition()); + assertEquals(Optional.of("region"), s3AccessPointResource.region()); + assertEquals("accesspoint", s3AccessPointResource.type()); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithBlankRegion() { + S3AccessPointResource.builder() + .accessPointName("access_point-name") + .accountId("account-id") + .partition("partition") + .region("") + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithBlankPartition() { + S3AccessPointResource.builder() + .accessPointName("access_point-name") + .accountId("account-id") + .region("region") + .partition("") + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithBlankAccountId() { + S3AccessPointResource.builder() + .accessPointName("access_point-name") + .partition("partition") + .region("region") + .accountId("") + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithBlankAccessPointName() { + S3AccessPointResource.builder() + .accountId("account-id") + .partition("partition") + .region("region") + .accessPointName("") + .build(); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingRegion() { + S3AccessPointResource.builder() + .accessPointName("access_point-name") + .accountId("account-id") + .partition("partition") + .build(); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingPartition() { + S3AccessPointResource.builder() + .accessPointName("access_point-name") + .accountId("account-id") + .region("region") + .build(); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingAccountId() { + S3AccessPointResource.builder() + .accessPointName("access_point-name") + .partition("partition") + .region("region") + .build(); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingAccessPointName() { + S3AccessPointResource.builder() + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + } + + @Test + public void buildWithSetters() { + S3AccessPointResource.Builder builder = S3AccessPointResource.builder(); + builder.setAccessPointName("access_point-name"); + builder.setAccountId("account-id"); + builder.setPartition("partition"); + builder.setRegion("region"); + S3AccessPointResource s3AccessPointResource = builder.build(); + + assertEquals("access_point-name", s3AccessPointResource.accessPointName()); + assertEquals(Optional.of("account-id"), s3AccessPointResource.accountId()); + assertEquals(Optional.of("partition"), s3AccessPointResource.partition()); + assertEquals(Optional.of("region"), s3AccessPointResource.region()); + assertEquals("accesspoint", s3AccessPointResource.type()); + } + + @Test + public void equals_allProperties() { + S3AccessPointResource s3BucketResource1 = S3AccessPointResource.builder() + .accessPointName("access_point") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3AccessPointResource s3BucketResource2 = S3AccessPointResource.builder() + .accessPointName("access_point") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3AccessPointResource s3BucketResource3 = S3AccessPointResource.builder() + .accessPointName("access_point") + .accountId("account-id") + .partition("different-partition") + .region("region") + .build(); + + assertEquals(s3BucketResource1, s3BucketResource2); + assertNotEquals(s3BucketResource1, s3BucketResource3); + } + + @Test + public void hashcode_allProperties() { + S3AccessPointResource s3BucketResource1 = S3AccessPointResource.builder() + .accessPointName("access_point") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3AccessPointResource s3BucketResource2 = S3AccessPointResource.builder() + .accessPointName("access_point") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3AccessPointResource s3BucketResource3 = S3AccessPointResource.builder() + .accessPointName("access_point") + .accountId("account-id") + .partition("different-partition") + .region("region") + .build(); + + assertEquals(s3BucketResource1.hashCode(), s3BucketResource2.hashCode()); + assertNotEquals(s3BucketResource1.hashCode(), s3BucketResource3.hashCode()); + } +} \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3ArnConverterTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3ArnConverterTest.java new file mode 100644 index 000000000000..c8f72172e229 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3ArnConverterTest.java @@ -0,0 +1,261 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; + +import java.util.Optional; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import software.amazon.awssdk.arns.Arn; + +public class S3ArnConverterTest { + private static final S3ArnConverter S3_ARN_PARSER = S3ArnConverter.create(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Test + public void parseArn_object_v2Arn() { + S3Resource resource = S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("object:bucket/key") + .build()); + + assertThat(resource, instanceOf(S3ObjectResource.class)); + + S3ObjectResource s3ObjectResource = (S3ObjectResource) resource; + assertThat(s3ObjectResource.bucketName(), is("bucket")); + assertThat(s3ObjectResource.key(), is("key")); + assertThat(s3ObjectResource.accountId(), is(Optional.of("123456789012"))); + assertThat(s3ObjectResource.partition(), is(Optional.of("aws"))); + assertThat(s3ObjectResource.region(), is(Optional.of("us-east-1"))); + assertThat(s3ObjectResource.type(), is(S3ResourceType.OBJECT.toString())); + } + + @Test + public void parseArn_object_v1Arn() { + S3Resource resource = S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .resource("bucket/key") + .build()); + + assertThat(resource, instanceOf(S3ObjectResource.class)); + + S3ObjectResource s3ObjectResource = (S3ObjectResource) resource; + assertThat(s3ObjectResource.bucketName(), is("bucket")); + assertThat(s3ObjectResource.key(), is("key")); + assertThat(s3ObjectResource.accountId(), is(Optional.empty())); + assertThat(s3ObjectResource.partition(), is(Optional.of("aws"))); + assertThat(s3ObjectResource.region(), is(Optional.empty())); + assertThat(s3ObjectResource.type(), is(S3ResourceType.OBJECT.toString())); + } + + @Test + public void parseArn_accessPoint() { + S3Resource resource = S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("accesspoint:accesspoint-name") + .build()); + + assertThat(resource, instanceOf(S3AccessPointResource.class)); + + S3AccessPointResource s3EndpointResource = (S3AccessPointResource) resource; + assertThat(s3EndpointResource.accessPointName(), is("accesspoint-name")); + assertThat(s3EndpointResource.accountId(), is(Optional.of("123456789012"))); + assertThat(s3EndpointResource.partition(), is(Optional.of("aws"))); + assertThat(s3EndpointResource.region(), is(Optional.of("us-east-1"))); + assertThat(s3EndpointResource.type(), is(S3ResourceType.ACCESS_POINT.toString())); + } + + @Test + public void parseArn_accessPoint_withQualifier() { + S3Resource resource = S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("accesspoint:accesspoint-name:1214234234") + .build()); + + assertThat(resource, instanceOf(S3AccessPointResource.class)); + + S3AccessPointResource s3EndpointResource = (S3AccessPointResource) resource; + assertThat(s3EndpointResource.accessPointName(), is("accesspoint-name")); + assertThat(s3EndpointResource.accountId(), is(Optional.of("123456789012"))); + assertThat(s3EndpointResource.partition(), is(Optional.of("aws"))); + assertThat(s3EndpointResource.region(), is(Optional.of("us-east-1"))); + assertThat(s3EndpointResource.type(), is(S3ResourceType.ACCESS_POINT.toString())); + } + + @Test + public void parseArn_v1Bucket() { + S3Resource resource = S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .resource("bucket-name") + .build()); + + assertThat(resource, instanceOf(S3BucketResource.class)); + + S3BucketResource s3BucketResource = (S3BucketResource) resource; + assertThat(s3BucketResource.bucketName(), is("bucket-name")); + assertThat(s3BucketResource.accountId(), is(Optional.empty())); + assertThat(s3BucketResource.partition(), is(Optional.of("aws"))); + assertThat(s3BucketResource.region(), is(Optional.empty())); + assertThat(s3BucketResource.type(), is(S3ResourceType.BUCKET.toString())); + } + + @Test + public void parseArn_v2Bucket() { + S3Resource resource = S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("bucket_name:bucket-name") + .build()); + + assertThat(resource, instanceOf(S3BucketResource.class)); + + S3BucketResource s3BucketResource = (S3BucketResource) resource; + assertThat(s3BucketResource.bucketName(), is("bucket-name")); + assertThat(s3BucketResource.accountId(), is(Optional.of("123456789012"))); + assertThat(s3BucketResource.partition(), is(Optional.of("aws"))); + assertThat(s3BucketResource.region(), is(Optional.of("us-east-1"))); + assertThat(s3BucketResource.type(), is(S3ResourceType.BUCKET.toString())); + } + + @Test + public void parseArn_unknownResource() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("ARN type"); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("unknown:foobar") + .build()); + } + + @Test + public void parseArn_bucket_noName() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("bucket_name:") + .build()); + } + + @Test + public void parseArn_accesspoint_noName() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("access_point:") + .build()); + } + + @Test + public void parseArn_object_v2Arn_noKey() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("object:bucket") + .build()); + } + + @Test + public void parseArn_object_v2Arn_emptyBucket() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("object:/key") + .build()); + } + + @Test + public void parseArn_object_v2Arn_emptyKey() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("object:bucket/") + .build()); + } + + @Test + public void parseArn_object_v1Arn_emptyKey() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .resource("bucket/") + .build()); + } + + @Test + public void parseArn_object_v1Arn_emptyBucket() { + exception.expect(IllegalArgumentException.class); + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .resource("/key") + .build()); + } + + @Test + public void parseArn_unknownType_throwsCorrectException() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("invalidType"); + + S3_ARN_PARSER.convertArn(Arn.builder() + .partition("aws") + .service("s3") + .region("us-east-1") + .accountId("123456789012") + .resource("invalidType:something") + .build()); + } +} \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3BucketResourceTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3BucketResourceTest.java new file mode 100644 index 000000000000..ed991c543d1e --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3BucketResourceTest.java @@ -0,0 +1,170 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import java.util.Optional; + +import org.junit.Test; + +public class S3BucketResourceTest { + @Test + public void buildWithAllPropertiesSet() { + S3BucketResource s3BucketResource = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + assertEquals("bucket", s3BucketResource.bucketName()); + assertEquals(Optional.of("account-id"), s3BucketResource.accountId()); + assertEquals(Optional.of("partition"), s3BucketResource.partition()); + assertEquals(Optional.of("region"), s3BucketResource.region()); + assertEquals("bucket_name", s3BucketResource.type()); + } + + @Test + public void toBuilder() { + S3BucketResource s3BucketResource = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("partition") + .region("region") + .build() + .toBuilder() + .build(); + + assertEquals("bucket", s3BucketResource.bucketName()); + assertEquals(Optional.of("account-id"), s3BucketResource.accountId()); + assertEquals(Optional.of("partition"), s3BucketResource.partition()); + assertEquals(Optional.of("region"), s3BucketResource.region()); + assertEquals("bucket_name", s3BucketResource.type()); + } + + @Test + public void buildWithSetters() { + S3BucketResource.Builder builder = S3BucketResource.builder(); + builder.setBucketName("bucket"); + builder.setAccountId("account-id"); + builder.setPartition("partition"); + builder.setRegion("region"); + S3BucketResource s3BucketResource = builder.build(); + + assertEquals("bucket", s3BucketResource.bucketName()); + assertEquals(Optional.of("account-id"), s3BucketResource.accountId()); + assertEquals(Optional.of("partition"), s3BucketResource.partition()); + assertEquals(Optional.of("region"), s3BucketResource.region()); + assertEquals("bucket_name", s3BucketResource.type()); + } + + @Test + public void buildWithMinimalPropertiesSet() { + S3BucketResource s3BucketResource = S3BucketResource.builder() + .bucketName("bucket") + .build(); + + assertEquals("bucket", s3BucketResource.bucketName()); + assertEquals(Optional.empty(), s3BucketResource.accountId()); + assertEquals(Optional.empty(), s3BucketResource.partition()); + assertEquals(Optional.empty(), s3BucketResource.region()); + assertEquals("bucket_name", s3BucketResource.type()); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingBucketName() { + S3BucketResource.builder().build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithBlankBucketName() { + S3BucketResource.builder().bucketName("").build(); + } + + @Test + public void equals_allProperties() { + S3BucketResource s3BucketResource1 = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3BucketResource s3BucketResource2 = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3BucketResource s3BucketResource3 = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("different-partition") + .region("region") + .build(); + + assertEquals(s3BucketResource1, s3BucketResource2); + assertNotEquals(s3BucketResource1, s3BucketResource3); + } + + @Test + public void equals_minimalProperties() { + S3BucketResource s3BucketResource1 = S3BucketResource.builder() + .bucketName("bucket") + .build(); + + S3BucketResource s3BucketResource2 = S3BucketResource.builder() + .bucketName("bucket") + .build(); + + S3BucketResource s3BucketResource3 = S3BucketResource.builder() + .bucketName("another-bucket") + .build(); + + assertEquals(s3BucketResource1, s3BucketResource2); + assertNotEquals(s3BucketResource1, s3BucketResource3); + } + + @Test + public void hashcode_allProperties() { + S3BucketResource s3BucketResource1 = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3BucketResource s3BucketResource2 = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3BucketResource s3BucketResource3 = S3BucketResource.builder() + .bucketName("bucket") + .accountId("account-id") + .partition("different-partition") + .region("region") + .build(); + + assertEquals(s3BucketResource1.hashCode(), s3BucketResource2.hashCode()); + assertNotEquals(s3BucketResource1.hashCode(), s3BucketResource3.hashCode()); + } +} \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3ObjectResourceTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3ObjectResourceTest.java new file mode 100644 index 000000000000..f235c6d4639f --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/resource/S3ObjectResourceTest.java @@ -0,0 +1,232 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.resource; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import java.util.Optional; + +import org.junit.Test; + +public class S3ObjectResourceTest { + @Test + public void buildWithAllPropertiesSet() { + S3ObjectResource s3ObjectResource = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + assertEquals("bucket", s3ObjectResource.bucketName()); + assertEquals("key", s3ObjectResource.key()); + assertEquals(Optional.of("account-id"), s3ObjectResource.accountId()); + assertEquals(Optional.of("partition"), s3ObjectResource.partition()); + assertEquals(Optional.of("region"), s3ObjectResource.region()); + assertEquals("object", s3ObjectResource.type()); + } + + @Test + public void toBuilder() { + S3ObjectResource s3ObjectResource = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("partition") + .region("region") + .build() + .toBuilder() + .build(); + + assertEquals("bucket", s3ObjectResource.bucketName()); + assertEquals("key", s3ObjectResource.key()); + assertEquals(Optional.of("account-id"), s3ObjectResource.accountId()); + assertEquals(Optional.of("partition"), s3ObjectResource.partition()); + assertEquals(Optional.of("region"), s3ObjectResource.region()); + assertEquals("object", s3ObjectResource.type()); + } + + @Test + public void buildWithSetters() { + S3ObjectResource.Builder builder = S3ObjectResource.builder(); + builder.setBucketName("bucket"); + builder.setKey("key"); + builder.setAccountId("account-id"); + builder.setPartition("partition"); + builder.setRegion("region"); + S3ObjectResource s3ObjectResource = builder.build(); + + assertEquals("bucket", s3ObjectResource.bucketName()); + assertEquals("key", s3ObjectResource.key()); + assertEquals(Optional.of("account-id"), s3ObjectResource.accountId()); + assertEquals(Optional.of("partition"), s3ObjectResource.partition()); + assertEquals(Optional.of("region"), s3ObjectResource.region()); + assertEquals("object", s3ObjectResource.type()); + } + + @Test + public void buildWithMinimalPropertiesSet() { + S3ObjectResource s3ObjectResource = S3ObjectResource.builder() + .partition("aws") + .bucketName("bucket") + .key("key") + .build(); + + assertEquals("bucket", s3ObjectResource.bucketName()); + assertEquals("key", s3ObjectResource.key()); + assertEquals(Optional.of("aws"), s3ObjectResource.partition()); + assertEquals(Optional.empty(), s3ObjectResource.accountId()); + assertEquals(Optional.empty(), s3ObjectResource.region()); + assertEquals("object", s3ObjectResource.type()); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingPartition() { + S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .build(); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingBucketName() { + S3ObjectResource.builder() + .partition("aws") + .key("key") + .build(); + } + + @Test(expected = NullPointerException.class) + public void buildWithMissingKey() { + S3ObjectResource.builder() + .partition("aws") + .bucketName("bucket-name") + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithEmptyPartition() { + S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .partition("") + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithEmptyBucketName() { + S3ObjectResource.builder() + .partition("aws") + .key("key") + .bucketName("") + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void buildWithEmptyKey() { + S3ObjectResource.builder() + .partition("aws") + .bucketName("bucket-name") + .key("") + .build(); + } + + @Test + public void equals_allProperties() { + S3ObjectResource s3ObjectResource1 = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3ObjectResource s3ObjectResource2 = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3ObjectResource s3ObjectResource3 = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("different-partition") + .region("region") + .build(); + + assertEquals(s3ObjectResource1, s3ObjectResource2); + assertNotEquals(s3ObjectResource1, s3ObjectResource3); + } + + @Test + public void equals_minimalProperties() { + S3ObjectResource s3ObjectResource1 = S3ObjectResource.builder() + .partition("aws") + .bucketName("bucket") + .key("key") + .build(); + + S3ObjectResource s3ObjectResource2 = S3ObjectResource.builder() + .partition("aws") + .bucketName("bucket") + .key("key") + .build(); + + S3ObjectResource s3ObjectResource3 = S3ObjectResource.builder() + .partition("aws") + .bucketName("another-bucket") + .key("key") + .build(); + + assertEquals(s3ObjectResource1, s3ObjectResource2); + assertNotEquals(s3ObjectResource1, s3ObjectResource3); + } + + @Test + public void hashcode_allProperties() { + S3ObjectResource s3ObjectResource1 = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3ObjectResource s3ObjectResource2 = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("partition") + .region("region") + .build(); + + S3ObjectResource s3ObjectResource3 = S3ObjectResource.builder() + .bucketName("bucket") + .key("key") + .accountId("account-id") + .partition("different-partition") + .region("region") + .build(); + + assertEquals(s3ObjectResource1.hashCode(), s3ObjectResource2.hashCode()); + assertNotEquals(s3ObjectResource1.hashCode(), s3ObjectResource3.hashCode()); + } +} \ No newline at end of file diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/ProfileUseArnRegionProviderTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/ProfileUseArnRegionProviderTest.java new file mode 100644 index 000000000000..eb6b3b27cc33 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/ProfileUseArnRegionProviderTest.java @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.in; +import static org.mockito.Matchers.any; +import static software.amazon.awssdk.profiles.ProfileFileSystemSetting.AWS_CONFIG_FILE; + +import java.util.Optional; +import java.util.StringJoiner; +import org.junit.After; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.utils.StringInputStream; + +public class ProfileUseArnRegionProviderTest { + private ProfileUseArnRegionProvider provider = ProfileUseArnRegionProvider.create(); + + @After + public void clearSystemProperty() { + System.clearProperty(AWS_CONFIG_FILE.property()); + } + + @Test + public void notSpecified_shouldReturnEmptyOptional() { + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.empty()); + } + + @Test + public void specifiedInConfigFile_shouldResolve() { + String configFile = getClass().getResource("UseArnRegionSet_true").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(TRUE)); + } + + @Test + public void configFile_mixedSpace() { + String configFile = getClass().getResource("UseArnRegionSet_mixedSpace").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(FALSE)); + } + + @Test + public void unsupportedValue_shouldThrowException() { + String configFile = getClass().getResource("UseArnRegionSet_unsupportedValue").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThatThrownBy(() -> provider.resolveUseArnRegion()).isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void commaNoSpace_shouldResolveCorrectly() { + String configFile = getClass().getResource("UseArnRegionSet_noSpace").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(FALSE)); + } + + @Test + public void specifiedInOverrideConfig_shouldUse() { + ExecutionInterceptor interceptor = Mockito.spy(AbstractExecutionInterceptor.class); + + String profileFileContent = + "[default]\n" + + "s3_use_arn_region = true\n"; + + ProfileFile profileFile = ProfileFile.builder() + .type(ProfileFile.Type.CONFIGURATION) + .content(new StringInputStream(profileFileContent)) + .build(); + + S3Client s3 = S3Client.builder() + .region(Region.US_WEST_2) + .credentialsProvider(AnonymousCredentialsProvider.create()) + .overrideConfiguration(c -> c.defaultProfileFile(profileFile) + .defaultProfileName("default") + .addExecutionInterceptor(interceptor) + .retryPolicy(r -> r.numRetries(0))) + .build(); + + String arn = "arn:aws:s3:us-banana-46:12345567890:accesspoint:foo"; + assertThatThrownBy(() -> s3.getObject(r -> r.bucket(arn).key("bar"))).isInstanceOf(SdkException.class); + + ArgumentCaptor context = ArgumentCaptor.forClass(Context.BeforeTransmission.class); + Mockito.verify(interceptor).beforeTransmission(context.capture(), any()); + + String host = context.getValue().httpRequest().host(); + assertThat(host).contains("us-banana-46"); + } + + public static abstract class AbstractExecutionInterceptor implements ExecutionInterceptor {} +} diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/SystemSettingsUseArnRegionProviderTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/SystemSettingsUseArnRegionProviderTest.java new file mode 100644 index 000000000000..021fd91751bb --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/SystemSettingsUseArnRegionProviderTest.java @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static software.amazon.awssdk.services.s3.S3SystemSetting.AWS_S3_USE_ARN_REGION; + +import java.util.Optional; +import org.junit.After; +import org.junit.Test; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; + +public class SystemSettingsUseArnRegionProviderTest { + private final SystemsSettingsUseArnRegionProvider provider = SystemsSettingsUseArnRegionProvider.create(); + private final EnvironmentVariableHelper helper = new EnvironmentVariableHelper(); + + @After + public void clearSystemProperty() { + System.clearProperty(AWS_S3_USE_ARN_REGION.property()); + helper.reset(); + } + + @Test + public void notSpecified_shouldReturnEmptyOptional() { + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.empty()); + } + + @Test + public void emptySystemProperties_shouldReturnEmptyOptional() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), ""); + assertThatThrownBy(() -> provider.resolveUseArnRegion()).isInstanceOf(IllegalStateException.class); + } + + @Test + public void specifiedInSystemProperties_shouldResolve() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), "false"); + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(FALSE)); + } + + @Test + public void specifiedInEnvironmentVariables_shouldResolve() { + helper.set(AWS_S3_USE_ARN_REGION.environmentVariable(), "true"); + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(TRUE)); + } + + @Test + public void specifiedInBothPlaces_SystemPropertiesShouldTakePrecedence() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), "true"); + helper.set(AWS_S3_USE_ARN_REGION.environmentVariable(), "false"); + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(TRUE)); + } + + @Test + public void mixedSpace_shouldResolveCorrectly() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), "tRuE"); + assertThat(provider.resolveUseArnRegion()).isEqualTo(Optional.of(TRUE)); + } +} diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProviderChainTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProviderChainTest.java new file mode 100644 index 000000000000..7ac12f7eda31 --- /dev/null +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionProviderChainTest.java @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.internal.usearnregion; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.profiles.ProfileFileSystemSetting.AWS_CONFIG_FILE; +import static software.amazon.awssdk.services.s3.S3SystemSetting.AWS_S3_USE_ARN_REGION; + +import java.util.Optional; +import org.junit.After; +import org.junit.Test; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; + +public class UseArnRegionProviderChainTest { + private final EnvironmentVariableHelper helper = new EnvironmentVariableHelper(); + + @After + public void clearSystemProperty() { + System.clearProperty(AWS_S3_USE_ARN_REGION.property()); + System.clearProperty(AWS_CONFIG_FILE.property()); + helper.reset(); + } + + @Test + public void notSpecified_shouldReturnEmptyOptional() { + assertThat(UseArnRegionProviderChain.create().resolveUseArnRegion()).isEqualTo(Optional.empty()); + } + + @Test + public void specifiedInBothProviders_systemPropertiesShouldTakePrecedence() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), "false"); + String configFile = getClass().getResource("UseArnRegionSet_true").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThat(UseArnRegionProviderChain.create().resolveUseArnRegion()).isEqualTo(Optional.of(Boolean.FALSE)); + } + + @Test + public void systemPropertiesThrowException_shouldUseConfigFile() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), "foobar"); + String configFile = getClass().getResource("UseArnRegionSet_true").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThat(UseArnRegionProviderChain.create().resolveUseArnRegion()).isEqualTo(Optional.of(Boolean.TRUE)); + } + + @Test + public void bothProvidersThrowException_shouldReturnEmpty() { + System.setProperty(AWS_S3_USE_ARN_REGION.property(), "foobar"); + String configFile = getClass().getResource("UseArnRegionSet_unsupportedValue").getFile(); + System.setProperty(AWS_CONFIG_FILE.property(), configFile); + + assertThat(UseArnRegionProviderChain.create().resolveUseArnRegion()).isEqualTo(Optional.empty()); + } +} diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequestTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequestTest.java index e659c4f71206..4436e66cd8b7 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequestTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/GetObjectPresignRequestTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; import static org.assertj.core.api.Assertions.assertThat; diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequestTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequestTest.java index d53f9c5bf424..b1e6a70d21ce 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequestTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/presigner/model/PresignedGetObjectRequestTest.java @@ -1,3 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.s3.presigner.model; diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/InterceptorTestUtils.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/InterceptorTestUtils.java index 3228d86825dd..06c7efa12347 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/InterceptorTestUtils.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/InterceptorTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/S3EndpointResolutionTest.java b/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/S3EndpointResolutionTest.java index 2387ca5d78af..26050f8c5b14 100644 --- a/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/S3EndpointResolutionTest.java +++ b/services/s3/src/test/java/software/amazon/awssdk/services/s3/utils/S3EndpointResolutionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -17,26 +17,31 @@ import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static software.amazon.awssdk.services.s3.S3MockUtils.mockListBucketsResponse; import static software.amazon.awssdk.services.s3.S3MockUtils.mockListObjectsResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import org.junit.Before; import org.junit.Test; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.SdkSystemSetting; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.signer.Signer; -import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.S3Configuration; import software.amazon.awssdk.services.s3.internal.handlers.EndpointAddressInterceptor; import software.amazon.awssdk.services.s3.model.ListObjectsRequest; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; import software.amazon.awssdk.testutils.service.http.MockHttpClient; +import software.amazon.awssdk.utils.StringInputStream; /** * Functional tests for various endpoint related behavior in S3. @@ -109,6 +114,53 @@ public void customEndpointProvided_UsesCustomEndpoint() throws Exception { .isEqualTo(URI.create(customEndpoint + "/")); } + @Test + public void accessPointArn_correctlyRewritesEndpoint() throws Exception { + URI customEndpoint = URI.create("https://foobar-12345678910.s3-accesspoint.ap-south-1.amazonaws.com"); + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + S3Client s3Client = clientBuilder().build(); + String accessPointArn = "arn:aws:s3:ap-south-1:12345678910:accesspoint:foobar"; + + s3Client.listObjects(ListObjectsRequest.builder().bucket(accessPointArn).build()); + + assertEndpointMatches(mockHttpClient.getLastRequest(), customEndpoint.toString()); + } + + @Test + public void accessPointArn_customEndpoint_throwsIllegalArgumentException() throws Exception { + URI customEndpoint = URI.create("https://foobar.amazonaws.com"); + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + S3Client s3Client = clientBuilder().endpointOverride(customEndpoint).build(); + String accessPointArn = "arn:aws:s3:ap-south-1:12345678910:accesspoint:foobar"; + + assertThatThrownBy(() -> s3Client.listObjects(ListObjectsRequest.builder().bucket(accessPointArn).build())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("endpoint override"); + } + + @Test + public void accessPointArn_differentRegion_useArnRegionFalse_throwsIllegalArgumentException() throws Exception { + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + S3Client s3Client = clientBuilder().build(); + String accessPointArn = "arn:aws:s3:us-west-2:12345678910:accesspoint:foobar"; + + assertThatThrownBy(() -> s3Client.listObjects(ListObjectsRequest.builder().bucket(accessPointArn).build())) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("region"); + } + + @Test + public void accessPointArn_differentRegion_useArnRegionTrue() throws Exception { + URI customEndpoint = URI.create("https://foobar-12345678910.s3-accesspoint.us-west-2.amazonaws.com"); + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + S3Client s3Client = clientBuilder().serviceConfiguration(b -> b.useArnRegionEnabled(true)).build(); + String accessPointArn = "arn:aws:s3:us-west-2:12345678910:accesspoint:foobar"; + + s3Client.listObjects(ListObjectsRequest.builder().bucket(accessPointArn).build()); + + assertEndpointMatches(mockHttpClient.getLastRequest(), customEndpoint.toString()); + } + /** * If a custom, non-s3 endpoint is used we revert to path style addressing. This is useful for alternative S3 implementations * like Ceph that do not support virtual style addressing. @@ -289,6 +341,95 @@ public void accelerateAndPathStyleEnabled_ThrowsIllegalArgumentException() { .build()); } + @Test + public void regionalSettingEnabled_usesRegionalIadEndpoint() throws UnsupportedEncodingException { + EnvironmentVariableHelper environmentVariableHelper = new EnvironmentVariableHelper(); + environmentVariableHelper.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), "regional"); + + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + + S3Client s3Client = S3Client.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"))) + .httpClient(mockHttpClient) + .region(Region.US_EAST_1) + .serviceConfiguration(S3Configuration.builder() + .pathStyleAccessEnabled(true) + .build()) + .build(); + try { + s3Client.listObjects(ListObjectsRequest.builder().bucket(BUCKET).build()); + assertThat(mockHttpClient.getLastRequest().getUri().getHost()).isEqualTo("s3.us-east-1.amazonaws.com"); + } finally { + environmentVariableHelper.reset(); + } + } + + @Test + public void regionalSettingEnabledViaProfile_usesRegionalIadEndpoint() throws UnsupportedEncodingException { + String profile = + "[profile test]\n" + + "s3_us_east_1_regional_endpoint = regional"; + + ProfileFile profileFile = ProfileFile.builder() + .content(new StringInputStream(profile)) + .type(ProfileFile.Type.CONFIGURATION) + .build(); + + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + + S3Client s3Client = S3Client.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"))) + .httpClient(mockHttpClient) + .region(Region.US_EAST_1) + .overrideConfiguration(c -> c.defaultProfileFile(profileFile) + .defaultProfileName("test")) + .serviceConfiguration(c -> c.pathStyleAccessEnabled(true)) + .build(); + + s3Client.listObjects(ListObjectsRequest.builder().bucket(BUCKET).build()); + assertThat(mockHttpClient.getLastRequest().getUri().getHost()).isEqualTo("s3.us-east-1.amazonaws.com"); + } + + @Test + public void regionalSettingDisabled_usesGlobalEndpoint() throws UnsupportedEncodingException { + EnvironmentVariableHelper environmentVariableHelper = new EnvironmentVariableHelper(); + environmentVariableHelper.set(SdkSystemSetting.AWS_S3_US_EAST_1_REGIONAL_ENDPOINT.environmentVariable(), "nonregional"); + + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + + S3Client s3Client = S3Client.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"))) + .httpClient(mockHttpClient) + .region(Region.US_EAST_1) + .serviceConfiguration(S3Configuration.builder() + .pathStyleAccessEnabled(true) + .build()) + .build(); + try { + s3Client.listObjects(ListObjectsRequest.builder().bucket(BUCKET).build()); + assertThat(mockHttpClient.getLastRequest().getUri().getHost()).isEqualTo("s3.amazonaws.com"); + } finally { + environmentVariableHelper.reset(); + } + } + + @Test + public void regionalSettingUnset_usesGlobalEndpoint() throws UnsupportedEncodingException { + mockHttpClient.stubNextResponse(mockListObjectsResponse()); + + S3Client s3Client = S3Client.builder() + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"))) + .httpClient(mockHttpClient) + .region(Region.US_EAST_1) + .serviceConfiguration(S3Configuration.builder() + .pathStyleAccessEnabled(true) + .build()) + .build(); + + s3Client.listObjects(ListObjectsRequest.builder().bucket(BUCKET).build()); + assertThat(mockHttpClient.getLastRequest().getUri().getHost()).isEqualTo("s3.amazonaws.com"); + } + /** * Assert that the provided request would have gone to the given endpoint. * diff --git a/services/s3/src/test/resources/log4j.properties b/services/s3/src/test/resources/log4j.properties index b821297c6731..012eb6e372f3 100644 --- a/services/s3/src/test/resources/log4j.properties +++ b/services/s3/src/test/resources/log4j.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_mixedSpace b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_mixedSpace new file mode 100644 index 000000000000..c7556917d2f2 --- /dev/null +++ b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_mixedSpace @@ -0,0 +1,2 @@ +[default] +s3_use_arn_region=fAlSE diff --git a/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_noSpace b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_noSpace new file mode 100644 index 000000000000..5a3114eaf6db --- /dev/null +++ b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_noSpace @@ -0,0 +1,2 @@ +[default] +s3_use_arn_region=false diff --git a/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_true b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_true new file mode 100644 index 000000000000..a70cbb59a9ed --- /dev/null +++ b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_true @@ -0,0 +1,2 @@ +[default] +s3_use_arn_region = true diff --git a/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_unsupportedValue b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_unsupportedValue new file mode 100644 index 000000000000..72a689233754 --- /dev/null +++ b/services/s3/src/test/resources/software/amazon/awssdk/services/s3/internal/usearnregion/UseArnRegionSet_unsupportedValue @@ -0,0 +1,2 @@ +[default] +s3_use_arn_region=unsupported-value diff --git a/services/s3control/pom.xml b/services/s3control/pom.xml index 223d801ab612..02cee30492be 100644 --- a/services/s3control/pom.xml +++ b/services/s3control/pom.xml @@ -1,6 +1,6 @@ commons-io diff --git a/services/s3control/src/it/java/software.amazon.awssdk.services.s3control/S3ControlIntegrationTest.java b/services/s3control/src/it/java/software.amazon.awssdk.services.s3control/S3ControlIntegrationTest.java index 6cf33e0195b2..48954def06cd 100644 --- a/services/s3control/src/it/java/software.amazon.awssdk.services.s3control/S3ControlIntegrationTest.java +++ b/services/s3control/src/it/java/software.amazon.awssdk.services.s3control/S3ControlIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/S3ControlConfiguration.java b/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/S3ControlConfiguration.java index 0c234733cb51..090f949ea499 100644 --- a/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/S3ControlConfiguration.java +++ b/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/S3ControlConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import software.amazon.awssdk.annotations.SdkPublicApi; import software.amazon.awssdk.annotations.ThreadSafe; import software.amazon.awssdk.core.ServiceConfiguration; +import software.amazon.awssdk.profiles.ProfileFile; import software.amazon.awssdk.utils.builder.CopyableBuilder; import software.amazon.awssdk.utils.builder.ToCopyableBuilder; @@ -42,12 +43,16 @@ public final class S3ControlConfiguration implements ServiceConfiguration, */ private static final boolean DEFAULT_DUALSTACK_ENABLED = false; - private final boolean fipsModeEnabled; - private final boolean dualstackEnabled; + private final Boolean fipsModeEnabled; + private final Boolean dualstackEnabled; + private final ProfileFile profileFile; + private final String profileName; private S3ControlConfiguration(DefaultS3ServiceConfigurationBuilder builder) { - this.dualstackEnabled = resolveBoolean(builder.dualstackEnabled, DEFAULT_DUALSTACK_ENABLED); - this.fipsModeEnabled = resolveBoolean(builder.fipsModeEnabled, DEFAULT_FIPS_MODE_ENABLED); + this.dualstackEnabled = builder.dualstackEnabled; + this.fipsModeEnabled = builder.fipsModeEnabled; + this.profileFile = builder.profileFile; + this.profileName = builder.profileName; } /** @@ -64,7 +69,7 @@ public static Builder builder() { * @return True if client will use FIPS mode. */ public boolean fipsModeEnabled() { - return fipsModeEnabled; + return resolveBoolean(fipsModeEnabled, DEFAULT_FIPS_MODE_ENABLED); } /** @@ -81,7 +86,7 @@ public boolean fipsModeEnabled() { * @return True if the client will use the dualstack endpoints */ public boolean dualstackEnabled() { - return dualstackEnabled; + return resolveBoolean(dualstackEnabled, DEFAULT_DUALSTACK_ENABLED); } private boolean resolveBoolean(Boolean suppliedValue, boolean defaultValue) { @@ -92,11 +97,15 @@ private boolean resolveBoolean(Boolean suppliedValue, boolean defaultValue) { public Builder toBuilder() { return builder() .dualstackEnabled(dualstackEnabled) - .fipsModeEnabled(fipsModeEnabled); + .fipsModeEnabled(fipsModeEnabled) + .profileFile(profileFile) + .profileName(profileName); } @NotThreadSafe public interface Builder extends CopyableBuilder { + Boolean dualstackEnabled(); + /** * Option to enable using the dualstack endpoints when accessing S3. Dualstack * should be enabled if you want to use IPv6. @@ -109,6 +118,8 @@ public interface Builder extends CopyableBuilder HOSTNAME_MAX_LENGTH) { + throw new IllegalArgumentException( + String.format("An argument has been passed that is not valid: the '%s' " + + "component exceeds the maximum length of %d characters.", componentName, + HOSTNAME_MAX_LENGTH)); + } + + Matcher m = HOSTNAME_COMPLIANT_PATTERN.matcher(component); + if (!m.matches()) { + throw new IllegalArgumentException( + String.format("An argument has been passed that is not valid: the '%s' " + + "component must only contain alphanumeric characters and dashes.", componentName)); + } + } } diff --git a/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptor.java b/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptor.java index f46a4627881d..38e1a815aee3 100644 --- a/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptor.java +++ b/services/s3control/src/main/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/s3control/src/main/resources/codegen-resources/paginators-1.json b/services/s3control/src/main/resources/codegen-resources/paginators-1.json index 886110429dda..b725a739c2b6 100644 --- a/services/s3control/src/main/resources/codegen-resources/paginators-1.json +++ b/services/s3control/src/main/resources/codegen-resources/paginators-1.json @@ -1,5 +1,10 @@ { "pagination": { + "ListAccessPoints": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults" + }, "ListJobs": { "input_token": "NextToken", "output_token": "NextToken", diff --git a/services/s3control/src/main/resources/codegen-resources/service-2.json b/services/s3control/src/main/resources/codegen-resources/service-2.json index 6314cf841fe4..b235e6e3618d 100644 --- a/services/s3control/src/main/resources/codegen-resources/service-2.json +++ b/services/s3control/src/main/resources/codegen-resources/service-2.json @@ -11,6 +11,19 @@ "uid":"s3control-2018-08-20" }, "operations":{ + "CreateAccessPoint":{ + "name":"CreateAccessPoint", + "http":{ + "method":"PUT", + "requestUri":"/v20180820/accesspoint/{name}" + }, + "input":{ + "shape":"CreateAccessPointRequest", + "locationName":"CreateAccessPointRequest", + "xmlNamespace":{"uri":"http://awss3control.amazonaws.com/doc/2018-08-20/"} + }, + "documentation":"

    Creates an access point and associates it with the specified bucket.

    " + }, "CreateJob":{ "name":"CreateJob", "http":{ @@ -31,6 +44,39 @@ ], "documentation":"

    Creates an Amazon S3 batch operations job.

    " }, + "DeleteAccessPoint":{ + "name":"DeleteAccessPoint", + "http":{ + "method":"DELETE", + "requestUri":"/v20180820/accesspoint/{name}" + }, + "input":{"shape":"DeleteAccessPointRequest"}, + "documentation":"

    Deletes the specified access point.

    " + }, + "DeleteAccessPointPolicy":{ + "name":"DeleteAccessPointPolicy", + "http":{ + "method":"DELETE", + "requestUri":"/v20180820/accesspoint/{name}/policy" + }, + "input":{"shape":"DeleteAccessPointPolicyRequest"}, + "documentation":"

    Deletes the access point policy for the specified access point.

    " + }, + "DeleteJobTagging":{ + "name":"DeleteJobTagging", + "http":{ + "method":"DELETE", + "requestUri":"/v20180820/jobs/{id}/tagging" + }, + "input":{"shape":"DeleteJobTaggingRequest"}, + "output":{"shape":"DeleteJobTaggingResult"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"} + ], + "documentation":"

    Delete the tags on a Amazon S3 batch operations job, if any.

    " + }, "DeletePublicAccessBlock":{ "name":"DeletePublicAccessBlock", "http":{ @@ -38,7 +84,7 @@ "requestUri":"/v20180820/configuration/publicAccessBlock" }, "input":{"shape":"DeletePublicAccessBlockRequest"}, - "documentation":"

    Deletes the block public access configuration for the specified account.

    " + "documentation":"

    Removes the PublicAccessBlock configuration for an Amazon Web Services account.

    " }, "DescribeJob":{ "name":"DescribeJob", @@ -56,6 +102,51 @@ ], "documentation":"

    Retrieves the configuration parameters and status for a batch operations job.

    " }, + "GetAccessPoint":{ + "name":"GetAccessPoint", + "http":{ + "method":"GET", + "requestUri":"/v20180820/accesspoint/{name}" + }, + "input":{"shape":"GetAccessPointRequest"}, + "output":{"shape":"GetAccessPointResult"}, + "documentation":"

    Returns configuration information about the specified access point.

    " + }, + "GetAccessPointPolicy":{ + "name":"GetAccessPointPolicy", + "http":{ + "method":"GET", + "requestUri":"/v20180820/accesspoint/{name}/policy" + }, + "input":{"shape":"GetAccessPointPolicyRequest"}, + "output":{"shape":"GetAccessPointPolicyResult"}, + "documentation":"

    Returns the access point policy associated with the specified access point.

    " + }, + "GetAccessPointPolicyStatus":{ + "name":"GetAccessPointPolicyStatus", + "http":{ + "method":"GET", + "requestUri":"/v20180820/accesspoint/{name}/policyStatus" + }, + "input":{"shape":"GetAccessPointPolicyStatusRequest"}, + "output":{"shape":"GetAccessPointPolicyStatusResult"}, + "documentation":"

    Indicates whether the specified access point currently has a policy that allows public access. For more information about public access through access points, see Managing Data Access with Amazon S3 Access Points in the Amazon Simple Storage Service Developer Guide.

    " + }, + "GetJobTagging":{ + "name":"GetJobTagging", + "http":{ + "method":"GET", + "requestUri":"/v20180820/jobs/{id}/tagging" + }, + "input":{"shape":"GetJobTaggingRequest"}, + "output":{"shape":"GetJobTaggingResult"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"} + ], + "documentation":"

    Retrieve the tags on a Amazon S3 batch operations job.

    " + }, "GetPublicAccessBlock":{ "name":"GetPublicAccessBlock", "http":{ @@ -67,7 +158,17 @@ "errors":[ {"shape":"NoSuchPublicAccessBlockConfiguration"} ], - "documentation":"

    " + "documentation":"

    Retrieves the PublicAccessBlock configuration for an Amazon Web Services account.

    " + }, + "ListAccessPoints":{ + "name":"ListAccessPoints", + "http":{ + "method":"GET", + "requestUri":"/v20180820/accesspoint" + }, + "input":{"shape":"ListAccessPointsRequest"}, + "output":{"shape":"ListAccessPointsResult"}, + "documentation":"

    Returns a list of the access points currently associated with the specified bucket. You can retrieve up to 1000 access points per call. If the specified bucket has more than 1000 access points (or the number specified in maxResults, whichever is less), then the response will include a continuation token that you can use to list the additional access points.

    " }, "ListJobs":{ "name":"ListJobs", @@ -84,6 +185,39 @@ ], "documentation":"

    Lists current jobs and jobs that have ended within the last 30 days for the AWS account making the request.

    " }, + "PutAccessPointPolicy":{ + "name":"PutAccessPointPolicy", + "http":{ + "method":"PUT", + "requestUri":"/v20180820/accesspoint/{name}/policy" + }, + "input":{ + "shape":"PutAccessPointPolicyRequest", + "locationName":"PutAccessPointPolicyRequest", + "xmlNamespace":{"uri":"http://awss3control.amazonaws.com/doc/2018-08-20/"} + }, + "documentation":"

    Associates an access policy with the specified access point. Each access point can have only one policy, so a request made to this API replaces any existing policy associated with the specified access point.

    " + }, + "PutJobTagging":{ + "name":"PutJobTagging", + "http":{ + "method":"PUT", + "requestUri":"/v20180820/jobs/{id}/tagging" + }, + "input":{ + "shape":"PutJobTaggingRequest", + "locationName":"PutJobTaggingRequest", + "xmlNamespace":{"uri":"http://awss3control.amazonaws.com/doc/2018-08-20/"} + }, + "output":{"shape":"PutJobTaggingResult"}, + "errors":[ + {"shape":"InternalServiceException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyTagsException"} + ], + "documentation":"

    Replace the set of tags on a Amazon S3 batch operations job.

    " + }, "PutPublicAccessBlock":{ "name":"PutPublicAccessBlock", "http":{ @@ -91,7 +225,7 @@ "requestUri":"/v20180820/configuration/publicAccessBlock" }, "input":{"shape":"PutPublicAccessBlockRequest"}, - "documentation":"

    " + "documentation":"

    Creates or modifies the PublicAccessBlock configuration for an Amazon Web Services account.

    " }, "UpdateJobPriority":{ "name":"UpdateJobPriority", @@ -128,6 +262,45 @@ } }, "shapes":{ + "AccessPoint":{ + "type":"structure", + "required":[ + "Name", + "NetworkOrigin", + "Bucket" + ], + "members":{ + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of this access point.

    " + }, + "NetworkOrigin":{ + "shape":"NetworkOrigin", + "documentation":"

    Indicates whether this access point allows access from the public Internet. If VpcConfiguration is specified for this access point, then NetworkOrigin is VPC, and the access point doesn't allow access from the public Internet. Otherwise, NetworkOrigin is Internet, and the access point allows access from the public Internet, subject to the access point and bucket access policies.

    " + }, + "VpcConfiguration":{ + "shape":"VpcConfiguration", + "documentation":"

    The Virtual Private Cloud (VPC) configuration for this access point, if one exists.

    " + }, + "Bucket":{ + "shape":"BucketName", + "documentation":"

    The name of the bucket associated with this access point.

    " + } + }, + "documentation":"

    An access point used to access a bucket.

    " + }, + "AccessPointList":{ + "type":"list", + "member":{ + "shape":"AccessPoint", + "locationName":"AccessPoint" + } + }, + "AccessPointName":{ + "type":"string", + "max":50, + "min":3 + }, "AccountId":{ "type":"string", "max":64 @@ -141,7 +314,43 @@ "exception":true }, "Boolean":{"type":"boolean"}, + "BucketName":{ + "type":"string", + "max":255, + "min":3 + }, "ConfirmationRequired":{"type":"boolean"}, + "CreateAccessPointRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name", + "Bucket" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account ID for the owner of the bucket for which you want to create an access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name you want to assign to this access point.

    ", + "location":"uri", + "locationName":"name" + }, + "Bucket":{ + "shape":"BucketName", + "documentation":"

    The name of the bucket that you want to associate this access point with.

    " + }, + "VpcConfiguration":{ + "shape":"VpcConfiguration", + "documentation":"

    If you include this field, Amazon S3 restricts access to this access point to requests from the specified Virtual Private Cloud (VPC).

    " + }, + "PublicAccessBlockConfiguration":{"shape":"PublicAccessBlockConfiguration"} + } + }, "CreateJobRequest":{ "type":"structure", "required":[ @@ -194,6 +403,10 @@ "RoleArn":{ "shape":"IAMRoleArn", "documentation":"

    The Amazon Resource Name (ARN) for the Identity and Access Management (IAM) Role that batch operations will use to execute this job's operation on each object in the manifest.

    " + }, + "Tags":{ + "shape":"S3TagSet", + "documentation":"

    An optional set of tags to associate with the job when it is created.

    " } } }, @@ -206,13 +419,82 @@ } } }, + "CreationDate":{"type":"timestamp"}, + "DeleteAccessPointPolicyRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the account that owns the specified access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the access point whose policy you want to delete.

    ", + "location":"uri", + "locationName":"name" + } + } + }, + "DeleteAccessPointRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the account that owns the specified access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the access point you want to delete.

    ", + "location":"uri", + "locationName":"name" + } + } + }, + "DeleteJobTaggingRequest":{ + "type":"structure", + "required":[ + "AccountId", + "JobId" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the Amazon Web Services account associated with the Amazon S3 batch operations job you want to remove tags from.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "JobId":{ + "shape":"JobId", + "documentation":"

    The ID for the job whose tags you want to delete.

    ", + "location":"uri", + "locationName":"id" + } + } + }, + "DeleteJobTaggingResult":{ + "type":"structure", + "members":{ + } + }, "DeletePublicAccessBlockRequest":{ "type":"structure", "required":["AccountId"], "members":{ "AccountId":{ "shape":"AccountId", - "documentation":"

    The account ID for the AWS account whose block public access configuration you want to delete.

    ", + "documentation":"

    The account ID for the Amazon Web Services account whose PublicAccessBlock configuration you want to remove.

    ", "location":"header", "locationName":"x-amz-account-id" } @@ -253,12 +535,149 @@ "max":1024, "min":1 }, + "GetAccessPointPolicyRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the account that owns the specified access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the access point whose policy you want to retrieve.

    ", + "location":"uri", + "locationName":"name" + } + } + }, + "GetAccessPointPolicyResult":{ + "type":"structure", + "members":{ + "Policy":{ + "shape":"Policy", + "documentation":"

    The access point policy associated with the specified access point.

    " + } + } + }, + "GetAccessPointPolicyStatusRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the account that owns the specified access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the access point whose policy status you want to retrieve.

    ", + "location":"uri", + "locationName":"name" + } + } + }, + "GetAccessPointPolicyStatusResult":{ + "type":"structure", + "members":{ + "PolicyStatus":{ + "shape":"PolicyStatus", + "documentation":"

    Indicates the current policy status of the specified access point.

    " + } + } + }, + "GetAccessPointRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the account that owns the specified access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the access point whose configuration information you want to retrieve.

    ", + "location":"uri", + "locationName":"name" + } + } + }, + "GetAccessPointResult":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the specified access point.

    " + }, + "Bucket":{ + "shape":"BucketName", + "documentation":"

    The name of the bucket associated with the specified access point.

    " + }, + "NetworkOrigin":{ + "shape":"NetworkOrigin", + "documentation":"

    Indicates whether this access point allows access from the public Internet. If VpcConfiguration is specified for this access point, then NetworkOrigin is VPC, and the access point doesn't allow access from the public Internet. Otherwise, NetworkOrigin is Internet, and the access point allows access from the public Internet, subject to the access point and bucket access policies.

    " + }, + "VpcConfiguration":{ + "shape":"VpcConfiguration", + "documentation":"

    Contains the Virtual Private Cloud (VPC) configuration for the specified access point.

    " + }, + "PublicAccessBlockConfiguration":{"shape":"PublicAccessBlockConfiguration"}, + "CreationDate":{ + "shape":"CreationDate", + "documentation":"

    The date and time when the specified access point was created.

    " + } + } + }, + "GetJobTaggingRequest":{ + "type":"structure", + "required":[ + "AccountId", + "JobId" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the Amazon Web Services account associated with the Amazon S3 batch operations job you want to retrieve tags for.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "JobId":{ + "shape":"JobId", + "documentation":"

    The ID for the job whose tags you want to retrieve.

    ", + "location":"uri", + "locationName":"id" + } + } + }, + "GetJobTaggingResult":{ + "type":"structure", + "members":{ + "Tags":{ + "shape":"S3TagSet", + "documentation":"

    The set of tags associated with the job.

    " + } + } + }, "GetPublicAccessBlockOutput":{ "type":"structure", "members":{ "PublicAccessBlockConfiguration":{ "shape":"PublicAccessBlockConfiguration", - "documentation":"

    " + "documentation":"

    The PublicAccessBlock configuration currently in effect for this Amazon Web Services account.

    " } }, "payload":"PublicAccessBlockConfiguration" @@ -269,7 +688,7 @@ "members":{ "AccountId":{ "shape":"AccountId", - "documentation":"

    ", + "documentation":"

    The account ID for the Amazon Web Services account whose PublicAccessBlock configuration you want to retrieve.

    ", "location":"header", "locationName":"x-amz-account-id" } @@ -313,6 +732,7 @@ "documentation":"

    ", "exception":true }, + "IsPublic":{"type":"boolean"}, "JobArn":{ "type":"string", "max":1024, @@ -629,7 +1049,7 @@ "members":{ "Bucket":{ "shape":"S3BucketArnString", - "documentation":"

    The bucket where specified job-completion report will be stored.

    ", + "documentation":"

    The Amazon Resource Name (ARN) for the bucket where specified job-completion report will be stored.

    ", "box":true }, "Format":{ @@ -720,6 +1140,49 @@ }, "documentation":"

    Contains the configuration parameters for a Lambda Invoke operation.

    " }, + "ListAccessPointsRequest":{ + "type":"structure", + "required":["AccountId"], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account ID for owner of the bucket whose access points you want to list.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Bucket":{ + "shape":"BucketName", + "documentation":"

    The name of the bucket whose associated access points you want to list.

    ", + "location":"querystring", + "locationName":"bucket" + }, + "NextToken":{ + "shape":"NonEmptyMaxLength1024String", + "documentation":"

    A continuation token. If a previous call to ListAccessPoints returned a continuation token in the NextToken field, then providing that value here causes Amazon S3 to retrieve the next page of results.

    ", + "location":"querystring", + "locationName":"nextToken" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of access points that you want to include in the list. If the specified bucket has more than this number of access points, then the response will include a continuation token in the NextToken field that you can use to retrieve the next page of access points.

    ", + "location":"querystring", + "locationName":"maxResults" + } + } + }, + "ListAccessPointsResult":{ + "type":"structure", + "members":{ + "AccessPointList":{ + "shape":"AccessPointList", + "documentation":"

    Contains identification and configuration information for one or more access points associated with the specified bucket.

    " + }, + "NextToken":{ + "shape":"NonEmptyMaxLength1024String", + "documentation":"

    If the specified bucket has more access points than can be returned in one call to this API, then this field contains a continuation token that you can provide in subsequent calls to this API to retrieve additional access points.

    " + } + } + }, "ListJobsRequest":{ "type":"structure", "required":["AccountId"], @@ -773,12 +1236,19 @@ "max":1000, "min":1 }, + "NetworkOrigin":{ + "type":"string", + "enum":[ + "Internet", + "VPC" + ] + }, "NoSuchPublicAccessBlockConfiguration":{ "type":"structure", "members":{ "Message":{"shape":"NoSuchPublicAccessBlockConfigurationMessage"} }, - "documentation":"

    ", + "documentation":"

    Amazon S3 throws this exception if you make a GetPublicAccessBlock request against an account that doesn't have a PublicAccessBlockConfiguration set.

    ", "error":{"httpStatusCode":404}, "exception":true }, @@ -821,31 +1291,100 @@ "S3InitiateRestoreObject" ] }, + "Policy":{"type":"string"}, + "PolicyStatus":{ + "type":"structure", + "members":{ + "IsPublic":{ + "shape":"IsPublic", + "documentation":"

    ", + "locationName":"IsPublic" + } + }, + "documentation":"

    Indicates whether this access point policy is public. For more information about how Amazon S3 evaluates policies to determine whether they are public, see The Meaning of \"Public\" in the Amazon Simple Storage Service Developer Guide.

    " + }, "PublicAccessBlockConfiguration":{ "type":"structure", "members":{ "BlockPublicAcls":{ "shape":"Setting", - "documentation":"

    ", + "documentation":"

    Specifies whether Amazon S3 should block public access control lists (ACLs) for buckets in this account. Setting this element to TRUE causes the following behavior:

    • PUT Bucket acl and PUT Object acl calls fail if the specified ACL is public.

    • PUT Object calls fail if the request includes a public ACL.

    • PUT Bucket calls fail if the request includes a public ACL.

    Enabling this setting doesn't affect existing policies or ACLs.

    ", "locationName":"BlockPublicAcls" }, "IgnorePublicAcls":{ "shape":"Setting", - "documentation":"

    ", + "documentation":"

    Specifies whether Amazon S3 should ignore public ACLs for buckets in this account. Setting this element to TRUE causes Amazon S3 to ignore all public ACLs on buckets in this account and any objects that they contain.

    Enabling this setting doesn't affect the persistence of any existing ACLs and doesn't prevent new public ACLs from being set.

    ", "locationName":"IgnorePublicAcls" }, "BlockPublicPolicy":{ "shape":"Setting", - "documentation":"

    ", + "documentation":"

    Specifies whether Amazon S3 should block public bucket policies for buckets in this account. Setting this element to TRUE causes Amazon S3 to reject calls to PUT Bucket policy if the specified bucket policy allows public access.

    Enabling this setting doesn't affect existing bucket policies.

    ", "locationName":"BlockPublicPolicy" }, "RestrictPublicBuckets":{ "shape":"Setting", - "documentation":"

    ", + "documentation":"

    Specifies whether Amazon S3 should restrict public bucket policies for buckets in this account. Setting this element to TRUE restricts access to buckets with public policies to only AWS services and authorized users within this account.

    Enabling this setting doesn't affect previously stored bucket policies, except that public and cross-account access within any public bucket policy, including non-public delegation to specific accounts, is blocked.

    ", "locationName":"RestrictPublicBuckets" } }, - "documentation":"

    " + "documentation":"

    The PublicAccessBlock configuration that you want to apply to this Amazon S3 bucket. You can enable the configuration options in any combination. For more information about when Amazon S3 considers a bucket or object public, see The Meaning of \"Public\" in the Amazon Simple Storage Service Developer Guide.

    " + }, + "PutAccessPointPolicyRequest":{ + "type":"structure", + "required":[ + "AccountId", + "Name", + "Policy" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The AWS account ID for owner of the bucket associated with the specified access point.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "Name":{ + "shape":"AccessPointName", + "documentation":"

    The name of the access point that you want to associate with the specified policy.

    ", + "location":"uri", + "locationName":"name" + }, + "Policy":{ + "shape":"Policy", + "documentation":"

    The policy that you want to apply to the specified access point. For more information about access point policies, see Managing Data Access with Amazon S3 Access Points in the Amazon Simple Storage Service Developer Guide.

    " + } + } + }, + "PutJobTaggingRequest":{ + "type":"structure", + "required":[ + "AccountId", + "JobId", + "Tags" + ], + "members":{ + "AccountId":{ + "shape":"AccountId", + "documentation":"

    The account ID for the Amazon Web Services account associated with the Amazon S3 batch operations job you want to replace tags on.

    ", + "location":"header", + "locationName":"x-amz-account-id" + }, + "JobId":{ + "shape":"JobId", + "documentation":"

    The ID for the job whose tags you want to replace.

    ", + "location":"uri", + "locationName":"id" + }, + "Tags":{ + "shape":"S3TagSet", + "documentation":"

    The set of tags to associate with the job.

    " + } + } + }, + "PutJobTaggingResult":{ + "type":"structure", + "members":{ + } }, "PutPublicAccessBlockRequest":{ "type":"structure", @@ -856,13 +1395,13 @@ "members":{ "PublicAccessBlockConfiguration":{ "shape":"PublicAccessBlockConfiguration", - "documentation":"

    ", + "documentation":"

    The PublicAccessBlock configuration that you want to apply to the specified Amazon Web Services account.

    ", "locationName":"PublicAccessBlockConfiguration", "xmlNamespace":{"uri":"http://awss3control.amazonaws.com/doc/2018-08-20/"} }, "AccountId":{ "shape":"AccountId", - "documentation":"

    ", + "documentation":"

    The account ID for the Amazon Web Services account whose PublicAccessBlock configuration you want to set.

    ", "location":"header", "locationName":"x-amz-account-id" } @@ -986,10 +1525,22 @@ "shape":"KmsKeyArnString", "documentation":"

    " }, - "TargetKeyPrefix":{"shape":"NonEmptyMaxLength1024String"}, - "ObjectLockLegalHoldStatus":{"shape":"S3ObjectLockLegalHoldStatus"}, - "ObjectLockMode":{"shape":"S3ObjectLockMode"}, - "ObjectLockRetainUntilDate":{"shape":"TimeStamp"} + "TargetKeyPrefix":{ + "shape":"NonEmptyMaxLength1024String", + "documentation":"

    " + }, + "ObjectLockLegalHoldStatus":{ + "shape":"S3ObjectLockLegalHoldStatus", + "documentation":"

    " + }, + "ObjectLockMode":{ + "shape":"S3ObjectLockMode", + "documentation":"

    " + }, + "ObjectLockRetainUntilDate":{ + "shape":"TimeStamp", + "documentation":"

    " + } }, "documentation":"

    Contains the configuration parameters for a PUT Copy object operation. Amazon S3 batch operations passes each value through to the underlying PUT Copy object API. For more information about the parameters for this operation, see PUT Object - Copy.

    " }, @@ -1251,6 +1802,13 @@ "documentation":"

    ", "exception":true }, + "TooManyTagsException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ExceptionMessage"} + }, + "exception":true + }, "UpdateJobPriorityRequest":{ "type":"structure", "required":[ @@ -1346,6 +1904,22 @@ "documentation":"

    The reason that the specified job's status was updated.

    " } } + }, + "VpcConfiguration":{ + "type":"structure", + "required":["VpcId"], + "members":{ + "VpcId":{ + "shape":"VpcId", + "documentation":"

    If this field is specified, this access point will only allow connections from the specified VPC ID.

    " + } + }, + "documentation":"

    The Virtual Private Cloud (VPC) configuration for an access point.

    " + }, + "VpcId":{ + "type":"string", + "max":1024, + "min":1 } }, "documentation":"

    AWS S3 Control provides access to Amazon S3 control plane operations.

    " diff --git a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/EndpointAddressInterceptorTest.java b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/EndpointAddressInterceptorTest.java index b78646701afc..a064ebaa40fa 100644 --- a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/EndpointAddressInterceptorTest.java +++ b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/EndpointAddressInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ package software.amazon.awssdk.services.s3control.internal.interceptors; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.Optional; import org.junit.Before; @@ -50,6 +51,19 @@ public void setup() { .build(); } + @Test + public void modifyHttpRequest_illegalCharacterInAccountId_throwsException() { + SdkHttpRequest modifiedRequest = SdkHttpFullRequest.builder() + .appendHeader(X_AMZ_ACCOUNT_ID, "1234/#") + .protocol(Protocol.HTTPS.toString()) + .method(SdkHttpMethod.POST) + .host(S3ControlClient.serviceMetadata().endpointFor(Region.US_EAST_1).toString()) + .build(); + EndpointAddressInterceptor interceptor = new EndpointAddressInterceptor(); + assertThatThrownBy(() -> interceptor.modifyHttpRequest(new Context(modifiedRequest), new ExecutionAttributes())) + .isInstanceOf(IllegalArgumentException.class).hasMessageContaining("account id"); + } + @Test public void modifyHttpRequest_ResolvesCorrectHost_StandardSettings() { EndpointAddressInterceptor interceptor = new EndpointAddressInterceptor(); diff --git a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptorTest.java b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptorTest.java index 6e0b39d1d8aa..f64cb21f188a 100644 --- a/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptorTest.java +++ b/services/s3control/src/test/java/software/amazon/awssdk/services/s3control/internal/interceptors/PayloadSigningInterceptorTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/services/sagemaker/pom.xml b/services/sagemaker/pom.xml index 603171a8c593..40f673163d12 100644 --- a/services/sagemaker/pom.xml +++ b/services/sagemaker/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + sagemakera2iruntime + AWS Java SDK :: Services :: SageMaker A2I Runtime + The AWS Java SDK for SageMaker A2I Runtime module holds the client classes that are used for + communicating with SageMaker A2I Runtime. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.sagemakera2iruntime + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/sagemakera2iruntime/src/main/resources/codegen-resources/paginators-1.json b/services/sagemakera2iruntime/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..b19128c2626a --- /dev/null +++ b/services/sagemakera2iruntime/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,10 @@ +{ + "pagination": { + "ListHumanLoops": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "MaxResults", + "result_key": "HumanLoopSummaries" + } + } +} diff --git a/services/sagemakera2iruntime/src/main/resources/codegen-resources/service-2.json b/services/sagemakera2iruntime/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..701d101c785e --- /dev/null +++ b/services/sagemakera2iruntime/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,461 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-11-07", + "endpointPrefix":"a2i-runtime.sagemaker", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceFullName":"Amazon Augmented AI Runtime", + "serviceId":"SageMaker A2I Runtime", + "signatureVersion":"v4", + "signingName":"sagemaker", + "uid":"sagemaker-a2i-runtime-2019-11-07" + }, + "operations":{ + "DeleteHumanLoop":{ + "name":"DeleteHumanLoop", + "http":{ + "method":"DELETE", + "requestUri":"/human-loops/{HumanLoopName}" + }, + "input":{"shape":"DeleteHumanLoopRequest"}, + "output":{"shape":"DeleteHumanLoopResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Deletes the specified human loop for a flow definition.

    " + }, + "DescribeHumanLoop":{ + "name":"DescribeHumanLoop", + "http":{ + "method":"GET", + "requestUri":"/human-loops/{HumanLoopName}" + }, + "input":{"shape":"DescribeHumanLoopRequest"}, + "output":{"shape":"DescribeHumanLoopResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Returns information about the specified human loop.

    " + }, + "ListHumanLoops":{ + "name":"ListHumanLoops", + "http":{ + "method":"GET", + "requestUri":"/human-loops" + }, + "input":{"shape":"ListHumanLoopsRequest"}, + "output":{"shape":"ListHumanLoopsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Returns information about human loops, given the specified parameters. If a human loop was deleted, it will not be included.

    " + }, + "StartHumanLoop":{ + "name":"StartHumanLoop", + "http":{ + "method":"POST", + "requestUri":"/human-loops" + }, + "input":{"shape":"StartHumanLoopRequest"}, + "output":{"shape":"StartHumanLoopResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ThrottlingException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"InternalServerException"}, + {"shape":"ConflictException"} + ], + "documentation":"

    Starts a human loop, provided that at least one activation condition is met.

    " + }, + "StopHumanLoop":{ + "name":"StopHumanLoop", + "http":{ + "method":"POST", + "requestUri":"/human-loops/stop" + }, + "input":{"shape":"StopHumanLoopRequest"}, + "output":{"shape":"StopHumanLoopResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"ResourceNotFoundException"}, + {"shape":"ThrottlingException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Stops the specified human loop.

    " + } + }, + "shapes":{ + "ConflictException":{ + "type":"structure", + "members":{ + "Message":{"shape":"FailureReason"} + }, + "documentation":"

    Your request has the same name as another active human loop but has different input data. You cannot start two human loops with the same name and different input data.

    ", + "error":{"httpStatusCode":409}, + "exception":true + }, + "ContentClassifier":{ + "type":"string", + "enum":[ + "FreeOfPersonallyIdentifiableInformation", + "FreeOfAdultContent" + ] + }, + "ContentClassifiers":{ + "type":"list", + "member":{"shape":"ContentClassifier"}, + "max":256 + }, + "DeleteHumanLoopRequest":{ + "type":"structure", + "required":["HumanLoopName"], + "members":{ + "HumanLoopName":{ + "shape":"HumanLoopName", + "documentation":"

    The name of the human loop you want to delete.

    ", + "location":"uri", + "locationName":"HumanLoopName" + } + } + }, + "DeleteHumanLoopResponse":{ + "type":"structure", + "members":{ + } + }, + "DescribeHumanLoopRequest":{ + "type":"structure", + "required":["HumanLoopName"], + "members":{ + "HumanLoopName":{ + "shape":"HumanLoopName", + "documentation":"

    The unique name of the human loop.

    ", + "location":"uri", + "locationName":"HumanLoopName" + } + } + }, + "DescribeHumanLoopResponse":{ + "type":"structure", + "required":[ + "CreationTime", + "HumanLoopStatus", + "HumanLoopName", + "HumanLoopArn", + "FlowDefinitionArn" + ], + "members":{ + "CreationTime":{ + "shape":"Timestamp", + "documentation":"

    The creation time when Amazon Augmented AI created the human loop.

    " + }, + "FailureReason":{ + "shape":"String", + "documentation":"

    The reason why a human loop has failed. The failure reason is returned when the human loop status is Failed.

    " + }, + "FailureCode":{ + "shape":"String", + "documentation":"

    A failure code denoting a specific type of failure.

    " + }, + "HumanLoopStatus":{ + "shape":"HumanLoopStatus", + "documentation":"

    The status of the human loop. Valid values:

    " + }, + "HumanLoopName":{ + "shape":"HumanLoopName", + "documentation":"

    The name of the human loop.

    " + }, + "HumanLoopArn":{ + "shape":"HumanLoopArn", + "documentation":"

    The Amazon Resource Name (ARN) of the human loop.

    " + }, + "FlowDefinitionArn":{ + "shape":"FlowDefinitionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the flow definition.

    " + }, + "HumanLoopOutput":{ + "shape":"HumanLoopOutput", + "documentation":"

    An object containing information about the output of the human loop.

    " + } + } + }, + "FailureReason":{ + "type":"string", + "max":1024 + }, + "FlowDefinitionArn":{ + "type":"string", + "max":1024, + "pattern":"arn:aws[a-z\\-]*:sagemaker:[a-z0-9\\-]*:[0-9]{12}:flow-definition/.*" + }, + "HumanLoopArn":{ + "type":"string", + "max":1024, + "pattern":"arn:aws[a-z\\-]*:sagemaker:[a-z0-9\\-]*:[0-9]{12}:human-loop/.*" + }, + "HumanLoopDataAttributes":{ + "type":"structure", + "required":["ContentClassifiers"], + "members":{ + "ContentClassifiers":{ + "shape":"ContentClassifiers", + "documentation":"

    Declares that your content is free of personally identifiable information or adult content.

    Amazon SageMaker can restrict the Amazon Mechanical Turk workers who can view your task based on this information.

    " + } + }, + "documentation":"

    Attributes of the data specified by the customer. Use these to describe the data to be labeled.

    " + }, + "HumanLoopInput":{ + "type":"structure", + "required":["InputContent"], + "members":{ + "InputContent":{ + "shape":"InputContent", + "documentation":"

    Serialized input from the human loop. The input must be a string representation of a file in JSON format.

    " + } + }, + "documentation":"

    An object containing the human loop input in JSON format.

    " + }, + "HumanLoopName":{ + "type":"string", + "max":63, + "min":1, + "pattern":"^[a-z0-9](-*[a-z0-9])*$" + }, + "HumanLoopOutput":{ + "type":"structure", + "required":["OutputS3Uri"], + "members":{ + "OutputS3Uri":{ + "shape":"String", + "documentation":"

    The location of the Amazon S3 object where Amazon Augmented AI stores your human loop output.

    " + } + }, + "documentation":"

    Information about where the human output will be stored.

    " + }, + "HumanLoopStatus":{ + "type":"string", + "enum":[ + "InProgress", + "Failed", + "Completed", + "Stopped", + "Stopping" + ] + }, + "HumanLoopSummaries":{ + "type":"list", + "member":{"shape":"HumanLoopSummary"} + }, + "HumanLoopSummary":{ + "type":"structure", + "members":{ + "HumanLoopName":{ + "shape":"HumanLoopName", + "documentation":"

    The name of the human loop.

    " + }, + "HumanLoopStatus":{ + "shape":"HumanLoopStatus", + "documentation":"

    The status of the human loop. Valid values:

    " + }, + "CreationTime":{ + "shape":"Timestamp", + "documentation":"

    When Amazon Augmented AI created the human loop.

    " + }, + "FailureReason":{ + "shape":"FailureReason", + "documentation":"

    The reason why the human loop failed. A failure reason is returned only when the status of the human loop is Failed.

    " + }, + "FlowDefinitionArn":{ + "shape":"FlowDefinitionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the flow definition.

    " + } + }, + "documentation":"

    Summary information about the human loop.

    " + }, + "InputContent":{ + "type":"string", + "max":4194304 + }, + "InternalServerException":{ + "type":"structure", + "members":{ + "Message":{"shape":"FailureReason"} + }, + "documentation":"

    Your request could not be processed.

    ", + "error":{"httpStatusCode":500}, + "exception":true + }, + "ListHumanLoopsRequest":{ + "type":"structure", + "required":["FlowDefinitionArn"], + "members":{ + "CreationTimeAfter":{ + "shape":"Timestamp", + "documentation":"

    (Optional) The timestamp of the date when you want the human loops to begin in ISO 8601 format. For example, 2020-02-24.

    ", + "location":"querystring", + "locationName":"CreationTimeAfter" + }, + "CreationTimeBefore":{ + "shape":"Timestamp", + "documentation":"

    (Optional) The timestamp of the date before which you want the human loops to begin in ISO 8601 format. For example, 2020-02-24.

    ", + "location":"querystring", + "locationName":"CreationTimeBefore" + }, + "FlowDefinitionArn":{ + "shape":"FlowDefinitionArn", + "documentation":"

    The Amazon Resource Name (ARN) of a flow definition.

    ", + "location":"querystring", + "locationName":"FlowDefinitionArn" + }, + "SortOrder":{ + "shape":"SortOrder", + "documentation":"

    An optional value that specifies whether you want the results sorted in Ascending or Descending order.

    ", + "location":"querystring", + "locationName":"SortOrder" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to resume pagination.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "MaxResults":{ + "shape":"MaxResults", + "documentation":"

    The total number of items to return. If the total number of available items is more than the value specified in MaxResults, then a NextToken will be provided in the output that you can use to resume pagination.

    ", + "box":true, + "location":"querystring", + "locationName":"MaxResults" + } + } + }, + "ListHumanLoopsResponse":{ + "type":"structure", + "required":["HumanLoopSummaries"], + "members":{ + "HumanLoopSummaries":{ + "shape":"HumanLoopSummaries", + "documentation":"

    An array of objects containing information about the human loops.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token to resume pagination.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":100, + "min":1 + }, + "NextToken":{ + "type":"string", + "max":8192, + "pattern":".*" + }, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"FailureReason"} + }, + "documentation":"

    We were unable to find the requested resource.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"FailureReason"} + }, + "documentation":"

    You have exceeded your service quota. To perform the requested action, remove some of the relevant resources, or request a service quota increase.

    ", + "error":{"httpStatusCode":402}, + "exception":true + }, + "SortOrder":{ + "type":"string", + "enum":[ + "Ascending", + "Descending" + ] + }, + "StartHumanLoopRequest":{ + "type":"structure", + "required":[ + "HumanLoopName", + "FlowDefinitionArn", + "HumanLoopInput" + ], + "members":{ + "HumanLoopName":{ + "shape":"HumanLoopName", + "documentation":"

    The name of the human loop.

    " + }, + "FlowDefinitionArn":{ + "shape":"FlowDefinitionArn", + "documentation":"

    The Amazon Resource Name (ARN) of the flow definition.

    " + }, + "HumanLoopInput":{ + "shape":"HumanLoopInput", + "documentation":"

    An object containing information about the human loop.

    " + }, + "DataAttributes":{ + "shape":"HumanLoopDataAttributes", + "documentation":"

    Attributes of the data specified by the customer.

    " + } + } + }, + "StartHumanLoopResponse":{ + "type":"structure", + "members":{ + "HumanLoopArn":{ + "shape":"HumanLoopArn", + "documentation":"

    The Amazon Resource Name (ARN) of the human loop.

    " + } + } + }, + "StopHumanLoopRequest":{ + "type":"structure", + "required":["HumanLoopName"], + "members":{ + "HumanLoopName":{ + "shape":"HumanLoopName", + "documentation":"

    The name of the human loop you want to stop.

    " + } + } + }, + "StopHumanLoopResponse":{ + "type":"structure", + "members":{ + } + }, + "String":{"type":"string"}, + "ThrottlingException":{ + "type":"structure", + "members":{ + "Message":{"shape":"FailureReason"} + }, + "documentation":"

    Your request has exceeded the allowed amount of requests.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "Timestamp":{"type":"timestamp"}, + "ValidationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"FailureReason"} + }, + "documentation":"

    Your request was not valid. Check the syntax and try again.

    ", + "error":{"httpStatusCode":400}, + "exception":true + } + }, + "documentation":"

    Amazon Augmented AI (Augmented AI) (Preview) is a service that adds human judgment to any machine learning application. Human reviewers can take over when an AI application can't evaluate data with a high degree of confidence.

    From fraudulent bank transaction identification to document processing to image analysis, machine learning models can be trained to make decisions as well as or better than a human. Nevertheless, some decisions require contextual interpretation, such as when you need to decide whether an image is appropriate for a given audience. Content moderation guidelines are nuanced and highly dependent on context, and they vary between countries. When trying to apply AI in these situations, you can be forced to choose between \"ML only\" systems with unacceptably high error rates or \"human only\" systems that are expensive and difficult to scale, and that slow down decision making.

    This API reference includes information about API actions and data types you can use to interact with Augmented AI programmatically.

    You can create a flow definition against the Augmented AI API. Provide the Amazon Resource Name (ARN) of a flow definition to integrate AI service APIs, such as Textract.AnalyzeDocument and Rekognition.DetectModerationLabels. These AI services, in turn, invoke the StartHumanLoop API, which evaluates conditions under which humans will be invoked. If humans are required, Augmented AI creates a human loop. Results of human work are available asynchronously in Amazon Simple Storage Service (Amazon S3). You can use Amazon CloudWatch Events to detect human work results.

    You can find additional Augmented AI API documentation in the following reference guides: Amazon Rekognition, Amazon SageMaker, and Amazon Textract.

    " +} diff --git a/services/sagemakerruntime/pom.xml b/services/sagemakerruntime/pom.xml index 32a880718d48..cb2db7011048 100644 --- a/services/sagemakerruntime/pom.xml +++ b/services/sagemakerruntime/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + savingsplans + AWS Java SDK :: Services :: Savingsplans + The AWS Java SDK for Savingsplans module holds the client classes that are used for + communicating with Savingsplans. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.savingsplans + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/savingsplans/src/main/resources/codegen-resources/paginators-1.json b/services/savingsplans/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..5677bd8e4a2d --- /dev/null +++ b/services/savingsplans/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,4 @@ +{ + "pagination": { + } +} diff --git a/services/savingsplans/src/main/resources/codegen-resources/service-2.json b/services/savingsplans/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..7287a3b9697e --- /dev/null +++ b/services/savingsplans/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,1135 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-06-28", + "endpointPrefix":"savingsplans", + "globalEndpoint":"savingsplans.amazonaws.com", + "jsonVersion":"1.0", + "protocol":"rest-json", + "serviceAbbreviation":"AWSSavingsPlans", + "serviceFullName":"AWS Savings Plans", + "serviceId":"savingsplans", + "signatureVersion":"v4", + "uid":"savingsplans-2019-06-28" + }, + "operations":{ + "CreateSavingsPlan":{ + "name":"CreateSavingsPlan", + "http":{ + "method":"POST", + "requestUri":"/CreateSavingsPlan" + }, + "input":{"shape":"CreateSavingsPlanRequest"}, + "output":{"shape":"CreateSavingsPlanResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"}, + {"shape":"ServiceQuotaExceededException"} + ], + "documentation":"

    Creates a Savings Plan.

    " + }, + "DescribeSavingsPlanRates":{ + "name":"DescribeSavingsPlanRates", + "http":{ + "method":"POST", + "requestUri":"/DescribeSavingsPlanRates" + }, + "input":{"shape":"DescribeSavingsPlanRatesRequest"}, + "output":{"shape":"DescribeSavingsPlanRatesResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Describes the specified Savings Plans rates.

    " + }, + "DescribeSavingsPlans":{ + "name":"DescribeSavingsPlans", + "http":{ + "method":"POST", + "requestUri":"/DescribeSavingsPlans" + }, + "input":{"shape":"DescribeSavingsPlansRequest"}, + "output":{"shape":"DescribeSavingsPlansResponse"}, + "errors":[ + {"shape":"InternalServerException"}, + {"shape":"ValidationException"} + ], + "documentation":"

    Describes the specified Savings Plans.

    " + }, + "DescribeSavingsPlansOfferingRates":{ + "name":"DescribeSavingsPlansOfferingRates", + "http":{ + "method":"POST", + "requestUri":"/DescribeSavingsPlansOfferingRates" + }, + "input":{"shape":"DescribeSavingsPlansOfferingRatesRequest"}, + "output":{"shape":"DescribeSavingsPlansOfferingRatesResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Describes the specified Savings Plans offering rates.

    " + }, + "DescribeSavingsPlansOfferings":{ + "name":"DescribeSavingsPlansOfferings", + "http":{ + "method":"POST", + "requestUri":"/DescribeSavingsPlansOfferings" + }, + "input":{"shape":"DescribeSavingsPlansOfferingsRequest"}, + "output":{"shape":"DescribeSavingsPlansOfferingsResponse"}, + "errors":[ + {"shape":"ValidationException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Describes the specified Savings Plans offerings.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"POST", + "requestUri":"/ListTagsForResource" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Lists the tags for the specified resource.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/TagResource" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ServiceQuotaExceededException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Adds the specified tags to the specified resource.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"POST", + "requestUri":"/UntagResource" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"ResourceNotFoundException"}, + {"shape":"ValidationException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Removes the specified tags from the specified resource.

    " + } + }, + "shapes":{ + "Amount":{"type":"string"}, + "ClientToken":{"type":"string"}, + "CreateSavingsPlanRequest":{ + "type":"structure", + "required":[ + "savingsPlanOfferingId", + "commitment" + ], + "members":{ + "savingsPlanOfferingId":{ + "shape":"SavingsPlanOfferingId", + "documentation":"

    The ID of the offering.

    " + }, + "commitment":{ + "shape":"Amount", + "documentation":"

    The hourly commitment, in USD. This is a value between 0.001 and 1 million. You cannot specify more than three digits after the decimal point.

    " + }, + "upfrontPaymentAmount":{ + "shape":"Amount", + "documentation":"

    The up-front payment amount. This is a whole number between 50 and 99 percent of the total value of the Savings Plan. This parameter is supported only if the payment option is Partial Upfront.

    " + }, + "clientToken":{ + "shape":"ClientToken", + "documentation":"

    Unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

    ", + "idempotencyToken":true + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    One or more tags.

    " + } + } + }, + "CreateSavingsPlanResponse":{ + "type":"structure", + "members":{ + "savingsPlanId":{ + "shape":"SavingsPlanId", + "documentation":"

    The ID of the Savings Plan.

    " + } + } + }, + "CurrencyCode":{ + "type":"string", + "enum":[ + "CNY", + "USD" + ] + }, + "CurrencyList":{ + "type":"list", + "member":{"shape":"CurrencyCode"} + }, + "DescribeSavingsPlanRatesRequest":{ + "type":"structure", + "required":["savingsPlanId"], + "members":{ + "savingsPlanId":{ + "shape":"SavingsPlanId", + "documentation":"

    The ID of the Savings Plan.

    " + }, + "filters":{ + "shape":"SavingsPlanRateFilterList", + "documentation":"

    The filters.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token for the next page of results.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return with a single call. To retrieve additional results, make another call with the returned token value.

    " + } + } + }, + "DescribeSavingsPlanRatesResponse":{ + "type":"structure", + "members":{ + "savingsPlanId":{ + "shape":"SavingsPlanId", + "documentation":"

    The ID of the Savings Plan.

    " + }, + "searchResults":{ + "shape":"SavingsPlanRateList", + "documentation":"

    Information about the Savings Plans rates.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token to use to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "DescribeSavingsPlansOfferingRatesRequest":{ + "type":"structure", + "members":{ + "savingsPlanOfferingIds":{ + "shape":"UUIDs", + "documentation":"

    The IDs of the offerings.

    " + }, + "savingsPlanPaymentOptions":{ + "shape":"SavingsPlanPaymentOptionList", + "documentation":"

    The payment options.

    " + }, + "savingsPlanTypes":{ + "shape":"SavingsPlanTypeList", + "documentation":"

    The plan types.

    " + }, + "products":{ + "shape":"SavingsPlanProductTypeList", + "documentation":"

    The AWS products.

    " + }, + "serviceCodes":{ + "shape":"SavingsPlanRateServiceCodeList", + "documentation":"

    The services.

    " + }, + "usageTypes":{ + "shape":"SavingsPlanRateUsageTypeList", + "documentation":"

    The usage details of the line item in the billing report.

    " + }, + "operations":{ + "shape":"SavingsPlanRateOperationList", + "documentation":"

    The specific AWS operation for the line item in the billing report.

    " + }, + "filters":{ + "shape":"SavingsPlanOfferingRateFiltersList", + "documentation":"

    The filters.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token for the next page of results.

    " + }, + "maxResults":{ + "shape":"PageSize", + "documentation":"

    The maximum number of results to return with a single call. To retrieve additional results, make another call with the returned token value.

    " + } + } + }, + "DescribeSavingsPlansOfferingRatesResponse":{ + "type":"structure", + "members":{ + "searchResults":{ + "shape":"SavingsPlanOfferingRatesList", + "documentation":"

    Information about the Savings Plans offering rates.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token to use to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "DescribeSavingsPlansOfferingsRequest":{ + "type":"structure", + "members":{ + "offeringIds":{ + "shape":"UUIDs", + "documentation":"

    The IDs of the offerings.

    " + }, + "paymentOptions":{ + "shape":"SavingsPlanPaymentOptionList", + "documentation":"

    The payment options.

    " + }, + "productType":{ + "shape":"SavingsPlanProductType", + "documentation":"

    The product type.

    " + }, + "planTypes":{ + "shape":"SavingsPlanTypeList", + "documentation":"

    The plan type.

    " + }, + "durations":{ + "shape":"DurationsList", + "documentation":"

    The durations, in seconds.

    " + }, + "currencies":{ + "shape":"CurrencyList", + "documentation":"

    The currencies.

    " + }, + "descriptions":{ + "shape":"SavingsPlanDescriptionsList", + "documentation":"

    The descriptions.

    " + }, + "serviceCodes":{ + "shape":"SavingsPlanServiceCodeList", + "documentation":"

    The services.

    " + }, + "usageTypes":{ + "shape":"SavingsPlanUsageTypeList", + "documentation":"

    The usage details of the line item in the billing report.

    " + }, + "operations":{ + "shape":"SavingsPlanOperationList", + "documentation":"

    The specific AWS operation for the line item in the billing report.

    " + }, + "filters":{ + "shape":"SavingsPlanOfferingFiltersList", + "documentation":"

    The filters.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token for the next page of results.

    " + }, + "maxResults":{ + "shape":"PageSize", + "documentation":"

    The maximum number of results to return with a single call. To retrieve additional results, make another call with the returned token value.

    " + } + } + }, + "DescribeSavingsPlansOfferingsResponse":{ + "type":"structure", + "members":{ + "searchResults":{ + "shape":"SavingsPlanOfferingsList", + "documentation":"

    Information about the Savings Plans offerings.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token to use to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "DescribeSavingsPlansRequest":{ + "type":"structure", + "members":{ + "savingsPlanArns":{ + "shape":"SavingsPlanArnList", + "documentation":"

    The Amazon Resource Names (ARN) of the Savings Plans.

    " + }, + "savingsPlanIds":{ + "shape":"SavingsPlanIdList", + "documentation":"

    The IDs of the Savings Plans.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token for the next page of results.

    " + }, + "maxResults":{ + "shape":"MaxResults", + "documentation":"

    The maximum number of results to return with a single call. To retrieve additional results, make another call with the returned token value.

    " + }, + "states":{ + "shape":"SavingsPlanStateList", + "documentation":"

    The states.

    " + }, + "filters":{ + "shape":"SavingsPlanFilterList", + "documentation":"

    The filters.

    " + } + } + }, + "DescribeSavingsPlansResponse":{ + "type":"structure", + "members":{ + "savingsPlans":{ + "shape":"SavingsPlanList", + "documentation":"

    Information about the Savings Plans.

    " + }, + "nextToken":{ + "shape":"PaginationToken", + "documentation":"

    The token to use to retrieve the next page of results. This value is null when there are no more results to return.

    " + } + } + }, + "DurationsList":{ + "type":"list", + "member":{"shape":"SavingsPlansDuration"} + }, + "EC2InstanceFamily":{"type":"string"}, + "FilterValuesList":{ + "type":"list", + "member":{"shape":"JsonSafeFilterValueString"} + }, + "InternalServerException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    An unexpected error occurred.

    ", + "error":{"httpStatusCode":500}, + "exception":true + }, + "JsonSafeFilterValueString":{ + "type":"string", + "pattern":"^[a-zA-Z0-9_ \\/.\\:\\-\\(\\)]+$" + }, + "ListOfStrings":{ + "type":"list", + "member":{"shape":"String"} + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["resourceArn"], + "members":{ + "resourceArn":{ + "shape":"SavingsPlanArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + } + } + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "members":{ + "tags":{ + "shape":"TagMap", + "documentation":"

    Information about the tags.

    " + } + } + }, + "MaxResults":{ + "type":"integer", + "max":1000, + "min":1 + }, + "PageSize":{ + "type":"integer", + "max":1000, + "min":0 + }, + "PaginationToken":{ + "type":"string", + "max":1024, + "pattern":"^[A-Za-z0-9/=\\+]+$" + }, + "ParentSavingsPlanOffering":{ + "type":"structure", + "members":{ + "offeringId":{ + "shape":"UUID", + "documentation":"

    The ID of the offering.

    " + }, + "paymentOption":{ + "shape":"SavingsPlanPaymentOption", + "documentation":"

    The payment option.

    " + }, + "planType":{ + "shape":"SavingsPlanType", + "documentation":"

    The plan type.

    " + }, + "durationSeconds":{ + "shape":"SavingsPlansDuration", + "documentation":"

    The duration, in seconds.

    " + }, + "currency":{ + "shape":"CurrencyCode", + "documentation":"

    The currency.

    " + }, + "planDescription":{ + "shape":"SavingsPlanDescription", + "documentation":"

    The description.

    " + } + }, + "documentation":"

    Information about a Savings Plan offering.

    " + }, + "Region":{"type":"string"}, + "ResourceNotFoundException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    The specified resource was not found.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "SavingsPlan":{ + "type":"structure", + "members":{ + "offeringId":{ + "shape":"SavingsPlanOfferingId", + "documentation":"

    The ID of the offering.

    " + }, + "savingsPlanId":{ + "shape":"SavingsPlanId", + "documentation":"

    The ID of the Savings Plan.

    " + }, + "savingsPlanArn":{ + "shape":"SavingsPlanArn", + "documentation":"

    The Amazon Resource Name (ARN) of the Savings Plan.

    " + }, + "description":{ + "shape":"String", + "documentation":"

    The description.

    " + }, + "start":{ + "shape":"String", + "documentation":"

    The start time.

    " + }, + "end":{ + "shape":"String", + "documentation":"

    The end time.

    " + }, + "state":{ + "shape":"SavingsPlanState", + "documentation":"

    The state.

    " + }, + "region":{ + "shape":"Region", + "documentation":"

    The AWS Region.

    " + }, + "ec2InstanceFamily":{ + "shape":"EC2InstanceFamily", + "documentation":"

    The EC2 instance family.

    " + }, + "savingsPlanType":{ + "shape":"SavingsPlanType", + "documentation":"

    The plan type.

    " + }, + "paymentOption":{ + "shape":"SavingsPlanPaymentOption", + "documentation":"

    The payment option.

    " + }, + "productTypes":{ + "shape":"SavingsPlanProductTypeList", + "documentation":"

    The product types.

    " + }, + "currency":{ + "shape":"CurrencyCode", + "documentation":"

    The currency.

    " + }, + "commitment":{ + "shape":"Amount", + "documentation":"

    The hourly commitment, in USD.

    " + }, + "upfrontPaymentAmount":{ + "shape":"Amount", + "documentation":"

    The up-front payment amount.

    " + }, + "recurringPaymentAmount":{ + "shape":"Amount", + "documentation":"

    The recurring payment amount.

    " + }, + "termDurationInSeconds":{ + "shape":"TermDurationInSeconds", + "documentation":"

    The duration of the term, in seconds.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    One or more tags.

    " + } + }, + "documentation":"

    Information about a Savings Plan.

    " + }, + "SavingsPlanArn":{ + "type":"string", + "pattern":"arn:aws:[a-z]+:([a-z]{2}-[a-z]+-\\d{1}|):(\\d{12}):savingsplan\\/([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$" + }, + "SavingsPlanArnList":{ + "type":"list", + "member":{"shape":"SavingsPlanArn"}, + "max":100 + }, + "SavingsPlanDescription":{ + "type":"string", + "pattern":"^[a-zA-Z0-9_\\- ]+$" + }, + "SavingsPlanDescriptionsList":{ + "type":"list", + "member":{"shape":"SavingsPlanDescription"} + }, + "SavingsPlanFilter":{ + "type":"structure", + "members":{ + "name":{ + "shape":"SavingsPlansFilterName", + "documentation":"

    The filter name.

    " + }, + "values":{ + "shape":"ListOfStrings", + "documentation":"

    The filter value.

    " + } + }, + "documentation":"

    Information about a filter.

    " + }, + "SavingsPlanFilterList":{ + "type":"list", + "member":{"shape":"SavingsPlanFilter"} + }, + "SavingsPlanId":{"type":"string"}, + "SavingsPlanIdList":{ + "type":"list", + "member":{"shape":"SavingsPlanId"} + }, + "SavingsPlanList":{ + "type":"list", + "member":{"shape":"SavingsPlan"} + }, + "SavingsPlanOffering":{ + "type":"structure", + "members":{ + "offeringId":{ + "shape":"UUID", + "documentation":"

    The ID of the offering.

    " + }, + "productTypes":{ + "shape":"SavingsPlanProductTypeList", + "documentation":"

    The product type.

    " + }, + "planType":{ + "shape":"SavingsPlanType", + "documentation":"

    The plan type.

    " + }, + "description":{ + "shape":"SavingsPlanDescription", + "documentation":"

    The description.

    " + }, + "paymentOption":{ + "shape":"SavingsPlanPaymentOption", + "documentation":"

    The payment option.

    " + }, + "durationSeconds":{ + "shape":"SavingsPlansDuration", + "documentation":"

    The duration, in seconds.

    " + }, + "currency":{ + "shape":"CurrencyCode", + "documentation":"

    The currency.

    " + }, + "serviceCode":{ + "shape":"SavingsPlanServiceCode", + "documentation":"

    The service.

    " + }, + "usageType":{ + "shape":"SavingsPlanUsageType", + "documentation":"

    The usage details of the line item in the billing report.

    " + }, + "operation":{ + "shape":"SavingsPlanOperation", + "documentation":"

    The specific AWS operation for the line item in the billing report.

    " + }, + "properties":{ + "shape":"SavingsPlanOfferingPropertyList", + "documentation":"

    The properties.

    " + } + }, + "documentation":"

    Information about a Savings Plan offering.

    " + }, + "SavingsPlanOfferingFilterAttribute":{ + "type":"string", + "enum":[ + "region", + "instanceFamily" + ] + }, + "SavingsPlanOfferingFilterElement":{ + "type":"structure", + "members":{ + "name":{ + "shape":"SavingsPlanOfferingFilterAttribute", + "documentation":"

    The filter name.

    " + }, + "values":{ + "shape":"FilterValuesList", + "documentation":"

    The filter values.

    " + } + }, + "documentation":"

    Information about a filter.

    " + }, + "SavingsPlanOfferingFiltersList":{ + "type":"list", + "member":{"shape":"SavingsPlanOfferingFilterElement"} + }, + "SavingsPlanOfferingId":{"type":"string"}, + "SavingsPlanOfferingProperty":{ + "type":"structure", + "members":{ + "name":{ + "shape":"SavingsPlanOfferingPropertyKey", + "documentation":"

    The property name.

    " + }, + "value":{ + "shape":"JsonSafeFilterValueString", + "documentation":"

    The property value.

    " + } + }, + "documentation":"

    Information about a property.

    " + }, + "SavingsPlanOfferingPropertyKey":{ + "type":"string", + "enum":[ + "region", + "instanceFamily" + ] + }, + "SavingsPlanOfferingPropertyList":{ + "type":"list", + "member":{"shape":"SavingsPlanOfferingProperty"} + }, + "SavingsPlanOfferingRate":{ + "type":"structure", + "members":{ + "savingsPlanOffering":{ + "shape":"ParentSavingsPlanOffering", + "documentation":"

    The Savings Plan offering.

    " + }, + "rate":{ + "shape":"SavingsPlanRatePricePerUnit", + "documentation":"

    The Savings Plan rate.

    " + }, + "unit":{ + "shape":"SavingsPlanRateUnit", + "documentation":"

    The unit.

    " + }, + "productType":{ + "shape":"SavingsPlanProductType", + "documentation":"

    The product type.

    " + }, + "serviceCode":{ + "shape":"SavingsPlanRateServiceCode", + "documentation":"

    The service.

    " + }, + "usageType":{ + "shape":"SavingsPlanRateUsageType", + "documentation":"

    The usage details of the line item in the billing report.

    " + }, + "operation":{ + "shape":"SavingsPlanRateOperation", + "documentation":"

    The specific AWS operation for the line item in the billing report.

    " + }, + "properties":{ + "shape":"SavingsPlanOfferingRatePropertyList", + "documentation":"

    The properties.

    " + } + }, + "documentation":"

    Information about a Savings Plan offering rate.

    " + }, + "SavingsPlanOfferingRateFilterElement":{ + "type":"structure", + "members":{ + "name":{ + "shape":"SavingsPlanRateFilterAttribute", + "documentation":"

    The filter name.

    " + }, + "values":{ + "shape":"FilterValuesList", + "documentation":"

    The filter values.

    " + } + }, + "documentation":"

    Information about a filter.

    " + }, + "SavingsPlanOfferingRateFiltersList":{ + "type":"list", + "member":{"shape":"SavingsPlanOfferingRateFilterElement"} + }, + "SavingsPlanOfferingRateProperty":{ + "type":"structure", + "members":{ + "name":{ + "shape":"JsonSafeFilterValueString", + "documentation":"

    The property name.

    " + }, + "value":{ + "shape":"JsonSafeFilterValueString", + "documentation":"

    The property value.

    " + } + }, + "documentation":"

    Information about a property.

    " + }, + "SavingsPlanOfferingRatePropertyList":{ + "type":"list", + "member":{"shape":"SavingsPlanOfferingRateProperty"} + }, + "SavingsPlanOfferingRatesList":{ + "type":"list", + "member":{"shape":"SavingsPlanOfferingRate"} + }, + "SavingsPlanOfferingsList":{ + "type":"list", + "member":{"shape":"SavingsPlanOffering"} + }, + "SavingsPlanOperation":{ + "type":"string", + "max":255, + "pattern":"^[a-zA-Z0-9_ \\/.:-]*$" + }, + "SavingsPlanOperationList":{ + "type":"list", + "member":{"shape":"SavingsPlanOperation"} + }, + "SavingsPlanPaymentOption":{ + "type":"string", + "enum":[ + "All Upfront", + "Partial Upfront", + "No Upfront" + ] + }, + "SavingsPlanPaymentOptionList":{ + "type":"list", + "member":{"shape":"SavingsPlanPaymentOption"} + }, + "SavingsPlanProductType":{ + "type":"string", + "enum":[ + "EC2", + "Fargate", + "Lambda" + ] + }, + "SavingsPlanProductTypeList":{ + "type":"list", + "member":{"shape":"SavingsPlanProductType"} + }, + "SavingsPlanRate":{ + "type":"structure", + "members":{ + "rate":{ + "shape":"Amount", + "documentation":"

    The rate.

    " + }, + "currency":{ + "shape":"CurrencyCode", + "documentation":"

    The currency.

    " + }, + "unit":{ + "shape":"SavingsPlanRateUnit", + "documentation":"

    The unit.

    " + }, + "productType":{ + "shape":"SavingsPlanProductType", + "documentation":"

    The product type.

    " + }, + "serviceCode":{ + "shape":"SavingsPlanRateServiceCode", + "documentation":"

    The service.

    " + }, + "usageType":{ + "shape":"SavingsPlanRateUsageType", + "documentation":"

    The usage details of the line item in the billing report.

    " + }, + "operation":{ + "shape":"SavingsPlanRateOperation", + "documentation":"

    The specific AWS operation for the line item in the billing report.

    " + }, + "properties":{ + "shape":"SavingsPlanRatePropertyList", + "documentation":"

    The properties.

    " + } + }, + "documentation":"

    Information about a Savings Plan rate.

    " + }, + "SavingsPlanRateFilter":{ + "type":"structure", + "members":{ + "name":{ + "shape":"SavingsPlanRateFilterName", + "documentation":"

    The filter name.

    " + }, + "values":{ + "shape":"ListOfStrings", + "documentation":"

    The filter values.

    " + } + }, + "documentation":"

    Information about a filter.

    " + }, + "SavingsPlanRateFilterAttribute":{ + "type":"string", + "enum":[ + "region", + "instanceFamily", + "instanceType", + "productDescription", + "tenancy", + "productId" + ] + }, + "SavingsPlanRateFilterList":{ + "type":"list", + "member":{"shape":"SavingsPlanRateFilter"} + }, + "SavingsPlanRateFilterName":{ + "type":"string", + "enum":[ + "region", + "instanceType", + "productDescription", + "tenancy", + "productType", + "serviceCode", + "usageType", + "operation" + ] + }, + "SavingsPlanRateList":{ + "type":"list", + "member":{"shape":"SavingsPlanRate"} + }, + "SavingsPlanRateOperation":{ + "type":"string", + "max":255, + "pattern":"^[a-zA-Z0-9_ \\/.:-]*$" + }, + "SavingsPlanRateOperationList":{ + "type":"list", + "member":{"shape":"SavingsPlanRateOperation"} + }, + "SavingsPlanRatePricePerUnit":{"type":"string"}, + "SavingsPlanRateProperty":{ + "type":"structure", + "members":{ + "name":{ + "shape":"SavingsPlanRatePropertyKey", + "documentation":"

    The property name.

    " + }, + "value":{ + "shape":"JsonSafeFilterValueString", + "documentation":"

    The property value.

    " + } + }, + "documentation":"

    Information about a property.

    " + }, + "SavingsPlanRatePropertyKey":{ + "type":"string", + "enum":[ + "region", + "instanceType", + "instanceFamily", + "productDescription", + "tenancy" + ] + }, + "SavingsPlanRatePropertyList":{ + "type":"list", + "member":{"shape":"SavingsPlanRateProperty"} + }, + "SavingsPlanRateServiceCode":{ + "type":"string", + "enum":[ + "AmazonEC2", + "AmazonECS", + "AWSLambda" + ] + }, + "SavingsPlanRateServiceCodeList":{ + "type":"list", + "member":{"shape":"SavingsPlanRateServiceCode"} + }, + "SavingsPlanRateUnit":{ + "type":"string", + "enum":[ + "Hrs", + "Lambda-GB-Second", + "Request" + ] + }, + "SavingsPlanRateUsageType":{ + "type":"string", + "max":255, + "pattern":"^[a-zA-Z0-9_ \\/.:-]+$" + }, + "SavingsPlanRateUsageTypeList":{ + "type":"list", + "member":{"shape":"SavingsPlanRateUsageType"} + }, + "SavingsPlanServiceCode":{ + "type":"string", + "max":255, + "pattern":"^[a-zA-Z]+$" + }, + "SavingsPlanServiceCodeList":{ + "type":"list", + "member":{"shape":"SavingsPlanServiceCode"} + }, + "SavingsPlanState":{ + "type":"string", + "enum":[ + "payment-pending", + "payment-failed", + "active", + "retired" + ] + }, + "SavingsPlanStateList":{ + "type":"list", + "member":{"shape":"SavingsPlanState"} + }, + "SavingsPlanType":{ + "type":"string", + "enum":[ + "Compute", + "EC2Instance" + ] + }, + "SavingsPlanTypeList":{ + "type":"list", + "member":{"shape":"SavingsPlanType"} + }, + "SavingsPlanUsageType":{ + "type":"string", + "max":255, + "pattern":"^[a-zA-Z0-9_ \\/.:-]+$" + }, + "SavingsPlanUsageTypeList":{ + "type":"list", + "member":{"shape":"SavingsPlanUsageType"} + }, + "SavingsPlansDuration":{ + "type":"long", + "min":0 + }, + "SavingsPlansFilterName":{ + "type":"string", + "enum":[ + "region", + "ec2-instance-family", + "commitment", + "upfront", + "term", + "savings-plan-type", + "payment-option", + "start", + "end" + ] + }, + "ServiceQuotaExceededException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    A service quota has been exceeded.

    ", + "error":{"httpStatusCode":402}, + "exception":true + }, + "String":{"type":"string"}, + "TagKey":{"type":"string"}, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"} + }, + "TagMap":{ + "type":"map", + "key":{"shape":"TagKey"}, + "value":{"shape":"TagValue"} + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tags" + ], + "members":{ + "resourceArn":{ + "shape":"SavingsPlanArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + }, + "tags":{ + "shape":"TagMap", + "documentation":"

    One or more tags. For example, { \"tags\": {\"key1\":\"value1\", \"key2\":\"value2\"} }.

    " + } + } + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{"type":"string"}, + "TermDurationInSeconds":{"type":"long"}, + "UUID":{ + "type":"string", + "pattern":"^(([0-9a-f]+)(-?))+$" + }, + "UUIDs":{ + "type":"list", + "member":{"shape":"UUID"} + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "resourceArn", + "tagKeys" + ], + "members":{ + "resourceArn":{ + "shape":"SavingsPlanArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + }, + "tagKeys":{ + "shape":"TagKeyList", + "documentation":"

    The tag keys.

    " + } + } + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "ValidationException":{ + "type":"structure", + "required":["message"], + "members":{ + "message":{"shape":"String"} + }, + "documentation":"

    One of the input parameters is not valid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + } + }, + "documentation":"

    Savings Plans are a pricing model that offer significant savings on AWS usage (for example, on Amazon EC2 instances). You commit to a consistent amount of usage, in USD per hour, for a term of 1 or 3 years, and receive a lower price for that usage. For more information, see the AWS Savings Plans User Guide.

    " +} diff --git a/services/schemas/pom.xml b/services/schemas/pom.xml new file mode 100644 index 000000000000..4b7b358eef5a --- /dev/null +++ b/services/schemas/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + schemas + AWS Java SDK :: Services :: Schemas + The AWS Java SDK for Schemas module holds the client classes that are used for + communicating with Schemas. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.schemas + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/schemas/src/main/resources/codegen-resources/paginators-1.json b/services/schemas/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..ef2fe19d1957 --- /dev/null +++ b/services/schemas/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,34 @@ +{ + "pagination": { + "ListDiscoverers": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Discoverers" + }, + "ListRegistries": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Registries" + }, + "ListSchemaVersions": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "SchemaVersions" + }, + "ListSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Schemas" + }, + "SearchSchemas": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "Limit", + "result_key": "Schemas" + } + } +} diff --git a/services/schemas/src/main/resources/codegen-resources/service-2.json b/services/schemas/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..89dbc62272d3 --- /dev/null +++ b/services/schemas/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,2916 @@ +{ + "metadata": { + "apiVersion": "2019-12-02", + "endpointPrefix": "schemas", + "signingName": "schemas", + "serviceFullName": "Schemas", + "serviceId": "schemas", + "protocol": "rest-json", + "jsonVersion": "1.1", + "uid": "schemas-2019-12-02", + "signatureVersion": "v4" + }, + "operations": { + "CreateDiscoverer": { + "name": "CreateDiscoverer", + "http": { + "method": "POST", + "requestUri": "/v1/discoverers", + "responseCode": 201 + }, + "input": { + "shape": "CreateDiscovererRequest" + }, + "output": { + "shape": "CreateDiscovererResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "ConflictException" + } + ], + "documentation": "

    Creates a discoverer.

    " + }, + "CreateRegistry": { + "name": "CreateRegistry", + "http": { + "method": "POST", + "requestUri": "/v1/registries/name/{registryName}", + "responseCode": 201 + }, + "input": { + "shape": "CreateRegistryRequest" + }, + "output": { + "shape": "CreateRegistryResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "ConflictException" + } + ], + "documentation": "

    Creates a registry.

    " + }, + "CreateSchema": { + "name": "CreateSchema", + "http": { + "method": "POST", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}", + "responseCode": 201 + }, + "input": { + "shape": "CreateSchemaRequest" + }, + "output": { + "shape": "CreateSchemaResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    Creates a schema definition.

    " + }, + "DeleteDiscoverer": { + "name": "DeleteDiscoverer", + "http": { + "method": "DELETE", + "requestUri": "/v1/discoverers/id/{discovererId}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteDiscovererRequest" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Deletes a discoverer.

    " + }, + "DeleteRegistry": { + "name": "DeleteRegistry", + "http": { + "method": "DELETE", + "requestUri": "/v1/registries/name/{registryName}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteRegistryRequest" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Deletes a Registry.

    " + }, + "DeleteSchema": { + "name": "DeleteSchema", + "http": { + "method": "DELETE", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteSchemaRequest" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Delete a schema definition.

    " + }, + "DeleteSchemaVersion": { + "name": "DeleteSchemaVersion", + "http": { + "method": "DELETE", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}/version/{schemaVersion}", + "responseCode": 204 + }, + "input": { + "shape": "DeleteSchemaVersionRequest" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Delete the schema version definition

    " + }, + "DescribeCodeBinding": { + "name": "DescribeCodeBinding", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}/language/{language}", + "responseCode": 200 + }, + "input": { + "shape": "DescribeCodeBindingRequest" + }, + "output": { + "shape": "DescribeCodeBindingResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "TooManyRequestsException" + } + ], + "documentation": "

    Describe the code binding URI.

    " + }, + "DescribeDiscoverer": { + "name": "DescribeDiscoverer", + "http": { + "method": "GET", + "requestUri": "/v1/discoverers/id/{discovererId}", + "responseCode": 200 + }, + "input": { + "shape": "DescribeDiscovererRequest" + }, + "output": { + "shape": "DescribeDiscovererResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Describes the discoverer.

    " + }, + "DescribeRegistry": { + "name": "DescribeRegistry", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}", + "responseCode": 200 + }, + "input": { + "shape": "DescribeRegistryRequest" + }, + "output": { + "shape": "DescribeRegistryResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Describes the registry.

    " + }, + "DescribeSchema": { + "name": "DescribeSchema", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}", + "responseCode": 200 + }, + "input": { + "shape": "DescribeSchemaRequest" + }, + "output": { + "shape": "DescribeSchemaResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Retrieve the schema definition.

    " + }, + "GetCodeBindingSource": { + "name": "GetCodeBindingSource", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}/language/{language}/source", + "responseCode": 200 + }, + "input": { + "shape": "GetCodeBindingSourceRequest" + }, + "output": { + "shape": "GetCodeBindingSourceResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "TooManyRequestsException" + } + ], + "documentation": "

    Get the code binding source URI.

    " + }, + "GetDiscoveredSchema": { + "name": "GetDiscoveredSchema", + "http": { + "method": "POST", + "requestUri": "/v1/discover", + "responseCode": 200 + }, + "input": { + "shape": "GetDiscoveredSchemaRequest" + }, + "output": { + "shape": "GetDiscoveredSchemaResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    Get the discovered schema that was generated based on sampled events.

    " + }, + "ListDiscoverers": { + "name": "ListDiscoverers", + "http": { + "method": "GET", + "requestUri": "/v1/discoverers", + "responseCode": 200 + }, + "input": { + "shape": "ListDiscoverersRequest" + }, + "output": { + "shape": "ListDiscoverersResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    List the discoverers.

    " + }, + "ListRegistries": { + "name": "ListRegistries", + "http": { + "method": "GET", + "requestUri": "/v1/registries", + "responseCode": 200 + }, + "input": { + "shape": "ListRegistriesRequest" + }, + "output": { + "shape": "ListRegistriesResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    List the registries.

    " + }, + "ListSchemaVersions": { + "name": "ListSchemaVersions", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}/versions", + "responseCode": 200 + }, + "input": { + "shape": "ListSchemaVersionsRequest" + }, + "output": { + "shape": "ListSchemaVersionsResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Provides a list of the schema versions and related information.

    " + }, + "ListSchemas": { + "name": "ListSchemas", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}/schemas", + "responseCode": 200 + }, + "input": { + "shape": "ListSchemasRequest" + }, + "output": { + "shape": "ListSchemasResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    List the schemas.

    " + }, + "ListTagsForResource": { + "name": "ListTagsForResource", + "http": { + "method": "GET", + "requestUri": "/tags/{resource-arn}", + "responseCode": 200 + }, + "input": { + "shape": "ListTagsForResourceRequest" + }, + "output": { + "shape": "ListTagsForResourceResponse" + }, + "errors": [ + { + "shape": "NotFoundException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    Get tags for resource.

    " + }, + "LockServiceLinkedRole": { + "name": "LockServiceLinkedRole", + "http": { + "method": "POST", + "requestUri": "/slr-deletion/lock", + "responseCode": 200 + }, + "input": { + "shape": "LockServiceLinkedRoleRequest" + }, + "output": { + "shape": "LockServiceLinkedRoleResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "internal": true + }, + "PutCodeBinding": { + "name": "PutCodeBinding", + "http": { + "method": "POST", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}/language/{language}", + "responseCode": 202 + }, + "input": { + "shape": "PutCodeBindingRequest" + }, + "output": { + "shape": "PutCodeBindingResponse" + }, + "errors": [ + { + "shape": "GoneException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "TooManyRequestsException" + } + ], + "documentation": "

    Put code binding URI

    " + }, + "SearchSchemas": { + "name": "SearchSchemas", + "http": { + "method": "GET", + "requestUri": "/v1/registries/name/{registryName}/schemas/search", + "responseCode": 200 + }, + "input": { + "shape": "SearchSchemasRequest" + }, + "output": { + "shape": "SearchSchemasResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    Search the schemas

    " + }, + "StartDiscoverer": { + "name": "StartDiscoverer", + "http": { + "method": "POST", + "requestUri": "/v1/discoverers/id/{discovererId}/start", + "responseCode": 200 + }, + "input": { + "shape": "StartDiscovererRequest" + }, + "output": { + "shape": "StartDiscovererResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Starts the discoverer

    " + }, + "StopDiscoverer": { + "name": "StopDiscoverer", + "http": { + "method": "POST", + "requestUri": "/v1/discoverers/id/{discovererId}/stop", + "responseCode": 200 + }, + "input": { + "shape": "StopDiscovererRequest" + }, + "output": { + "shape": "StopDiscovererResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Stops the discoverer

    " + }, + "TagResource": { + "name": "TagResource", + "http": { + "method": "POST", + "requestUri": "/tags/{resource-arn}", + "responseCode": 204 + }, + "input": { + "shape": "TagResourceRequest" + }, + "errors": [ + { + "shape": "NotFoundException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    Add tags to a resource.

    " + }, + "UnlockServiceLinkedRole": { + "name": "UnlockServiceLinkedRole", + "http": { + "method": "POST", + "requestUri": "/slr-deletion/unlock", + "responseCode": 200 + }, + "input": { + "shape": "UnlockServiceLinkedRoleRequest" + }, + "output": { + "shape": "UnlockServiceLinkedRoleResponse" + }, + "errors": [ + { + "shape": "ServiceUnavailableException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "internal": true + }, + "UntagResource": { + "name": "UntagResource", + "http": { + "method": "DELETE", + "requestUri": "/tags/{resource-arn}", + "responseCode": 204 + }, + "input": { + "shape": "UntagResourceRequest" + }, + "errors": [ + { + "shape": "NotFoundException" + }, + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + } + ], + "documentation": "

    Removes tags from a resource.

    " + }, + "UpdateDiscoverer": { + "name": "UpdateDiscoverer", + "http": { + "method": "PUT", + "requestUri": "/v1/discoverers/id/{discovererId}", + "responseCode": 200 + }, + "input": { + "shape": "UpdateDiscovererRequest" + }, + "output": { + "shape": "UpdateDiscovererResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Updates the discoverer

    " + }, + "UpdateRegistry": { + "name": "UpdateRegistry", + "http": { + "method": "PUT", + "requestUri": "/v1/registries/name/{registryName}", + "responseCode": 200 + }, + "input": { + "shape": "UpdateRegistryRequest" + }, + "output": { + "shape": "UpdateRegistryResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "UnauthorizedException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Updates a registry.

    " + }, + "UpdateSchema": { + "name": "UpdateSchema", + "http": { + "method": "PUT", + "requestUri": "/v1/registries/name/{registryName}/schemas/name/{schemaName}", + "responseCode": 200 + }, + "input": { + "shape": "UpdateSchemaRequest" + }, + "output": { + "shape": "UpdateSchemaResponse" + }, + "errors": [ + { + "shape": "BadRequestException" + }, + { + "shape": "InternalServerErrorException" + }, + { + "shape": "ForbiddenException" + }, + { + "shape": "NotFoundException" + }, + { + "shape": "ServiceUnavailableException" + } + ], + "documentation": "

    Updates the schema definition

    " + } + }, + "shapes": { + "BadRequestException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 400 + } + }, + "CodeBindingOutput": { + "type": "structure", + "members": { + "CreationDate": { + "shape": "__timestampIso8601", + "documentation": "

    The time and date that the code binding was created.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that code bindings were modified.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema.

    " + }, + "Status": { + "shape": "CodeGenerationStatus", + "documentation": "

    The current status of code binding generation.

    " + } + } + }, + "CodeGenerationStatus": { + "type": "string", + "enum": [ + "CREATE_IN_PROGRESS", + "CREATE_COMPLETE", + "CREATE_FAILED" + ] + }, + "ConflictException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 409 + } + }, + "CreateDiscovererInput": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    A description for the discoverer.

    " + }, + "SourceArn": { + "shape": "__stringMin20Max1600", + "documentation": "

    The ARN of the event bus.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + }, + "required": [ + "SourceArn" + ] + }, + "CreateDiscovererRequest": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    A description for the discoverer.

    " + }, + "SourceArn": { + "shape": "__stringMin20Max1600", + "documentation": "

    The ARN of the event bus.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + }, + "required": [ + "SourceArn" + ] + }, + "CreateDiscovererResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the discoverer.

    " + }, + "DiscovererArn": { + "shape": "__string", + "documentation": "

    The ARN of the discoverer.

    " + }, + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "SourceArn": { + "shape": "__string", + "documentation": "

    The ARN of the event bus.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + } + }, + "CreateRegistryInput": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    A description of the registry to be created.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags to associate with the registry.

    " + } + } + }, + "CreateRegistryRequest": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    A description of the registry to be created.

    " + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags to associate with the registry.

    " + } + }, + "required": [ + "RegistryName" + ] + }, + "CreateRegistryResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the registry.

    " + }, + "RegistryArn": { + "shape": "__string", + "documentation": "

    The ARN of the registry.

    " + }, + "RegistryName": { + "shape": "__string", + "documentation": "

    The name of the registry.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the registry.

    " + } + } + }, + "CreateSchemaInput": { + "type": "structure", + "members": { + "Content": { + "shape": "__stringMin1Max100000" + }, + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    A description of the schema.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the schema.

    " + }, + "Type": { + "shape": "Type" + } + }, + "required": [ + "Type", + "Content" + ] + }, + "CreateSchemaRequest": { + "type": "structure", + "members": { + "Content": { + "shape": "__stringMin1Max100000" + }, + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    A description of the schema.

    " + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the schema.

    " + }, + "Type": { + "shape": "Type" + } + }, + "required": [ + "RegistryName", + "SchemaName", + "Type", + "Content" + ] + }, + "CreateSchemaResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the schema.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that schema was modified.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags" + }, + "Type": { + "shape": "__string", + "documentation": "

    The type of the schema.

    " + }, + "VersionCreatedDate": { + "shape": "__timestampIso8601", + "documentation": "

    The date the schema version was created.

    " + } + } + }, + "DeleteDiscovererRequest": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "location": "uri", + "locationName": "discovererId" + } + }, + "required": [ + "DiscovererId" + ] + }, + "DeleteRegistryRequest": { + "type": "structure", + "members": { + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + } + }, + "required": [ + "RegistryName" + ] + }, + "DeleteSchemaRequest": { + "type": "structure", + "members": { + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + } + }, + "required": [ + "RegistryName", + "SchemaName" + ] + }, + "DeleteSchemaVersionRequest": { + "type": "structure", + "members": { + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "SchemaVersion": { + "shape": "__string", + "location": "uri", + "locationName": "schemaVersion" + } + }, + "required": [ + "SchemaVersion", + "RegistryName", + "SchemaName" + ] + }, + "DescribeCodeBindingRequest": { + "type": "structure", + "members": { + "Language": { + "shape": "__string", + "location": "uri", + "locationName": "language" + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "SchemaVersion": { + "shape": "__string", + "location": "querystring", + "locationName": "schemaVersion" + } + }, + "required": [ + "RegistryName", + "SchemaName", + "Language" + ] + }, + "DescribeCodeBindingResponse": { + "type": "structure", + "members": { + "CreationDate": { + "shape": "__timestampIso8601", + "documentation": "

    The time and date that the code binding was created.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that code bindings were modified.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema.

    " + }, + "Status": { + "shape": "CodeGenerationStatus", + "documentation": "

    The current status of code binding generation.

    " + } + } + }, + "DescribeDiscovererRequest": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "location": "uri", + "locationName": "discovererId" + } + }, + "required": [ + "DiscovererId" + ] + }, + "DescribeDiscovererResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the discoverer.

    " + }, + "DiscovererArn": { + "shape": "__string", + "documentation": "

    The ARN of the discoverer.

    " + }, + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "SourceArn": { + "shape": "__string", + "documentation": "

    The ARN of the event bus.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + } + }, + "DescribeRegistryRequest": { + "type": "structure", + "members": { + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + } + }, + "required": [ + "RegistryName" + ] + }, + "DescribeRegistryResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the registry.

    " + }, + "RegistryArn": { + "shape": "__string", + "documentation": "

    The ARN of the registry.

    " + }, + "RegistryName": { + "shape": "__string", + "documentation": "

    The name of the registry.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the registry.

    " + } + } + }, + "DescribeSchemaOutput": { + "type": "structure", + "members": { + "Content": { + "shape": "__string" + }, + "Description": { + "shape": "__string", + "documentation": "

    The description of the schema.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that schema was modified.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + }, + "Type": { + "shape": "__string", + "documentation": "

    The type of the schema.

    " + }, + "VersionCreatedDate": { + "shape": "__timestampIso8601", + "documentation": "

    The date the schema version was created.

    " + } + } + }, + "DescribeSchemaRequest": { + "type": "structure", + "members": { + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "SchemaVersion": { + "shape": "__string", + "location": "querystring", + "locationName": "schemaVersion" + } + }, + "required": [ + "RegistryName", + "SchemaName" + ] + }, + "DescribeSchemaResponse": { + "type": "structure", + "members": { + "Content": { + "shape": "__string" + }, + "Description": { + "shape": "__string", + "documentation": "

    The description of the schema.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that schema was modified.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + }, + "Type": { + "shape": "__string", + "documentation": "

    The type of the schema.

    " + }, + "VersionCreatedDate": { + "shape": "__timestampIso8601", + "documentation": "

    The date the schema version was created.

    " + } + } + }, + "DiscovererOutput": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the discoverer.

    " + }, + "DiscovererArn": { + "shape": "__string", + "documentation": "

    The ARN of the discoverer.

    " + }, + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "SourceArn": { + "shape": "__string", + "documentation": "

    The ARN of the event bus.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + } + }, + "DiscovererState": { + "type": "string", + "enum": [ + "STARTED", + "STOPPED" + ] + }, + "DiscovererStateOutput": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + } + } + }, + "DiscovererSummary": { + "type": "structure", + "members": { + "DiscovererArn": { + "shape": "__string", + "documentation": "

    The ARN of the discoverer.

    " + }, + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "SourceArn": { + "shape": "__string", + "documentation": "

    The ARN of the event bus.

    " + }, + "State": { + "shape": "DiscovererState" + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + } + }, + "ErrorOutput": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ] + }, + "ForbiddenException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 403 + } + }, + "GetCodeBindingSourceOutput": { + "type": "string" + }, + "GetCodeBindingSourceRequest": { + "type": "structure", + "members": { + "Language": { + "shape": "__string", + "location": "uri", + "locationName": "language" + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "SchemaVersion": { + "shape": "__string", + "location": "querystring", + "locationName": "schemaVersion" + } + }, + "required": [ + "RegistryName", + "SchemaName", + "Language" + ] + }, + "GetCodeBindingSourceResponse": { + "type": "structure", + "members": { + "Body": { + "shape": "Body" + } + }, + "payload": "Body" + }, + "GetDiscoveredSchemaInput": { + "type": "structure", + "members": { + "Events": { + "shape": "__listOfGetDiscoveredSchemaVersionItemInput", + "documentation": "

    An array of strings that

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The type of event.

    " + } + }, + "required": [ + "Type", + "Events" + ] + }, + "GetDiscoveredSchemaOutput": { + "type": "structure", + "members": { + "Content": { + "shape": "__string" + } + } + }, + "GetDiscoveredSchemaRequest": { + "type": "structure", + "members": { + "Events": { + "shape": "__listOfGetDiscoveredSchemaVersionItemInput", + "documentation": "

    An array of strings that

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The type of event.

    " + } + }, + "required": [ + "Type", + "Events" + ] + }, + "GetDiscoveredSchemaResponse": { + "type": "structure", + "members": { + "Content": { + "shape": "__string" + } + } + }, + "GetDiscoveredSchemaVersionItemInput": { + "type": "string", + "min": 1, + "max": 100000 + }, + "GoneException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 410 + } + }, + "InternalServerErrorException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 500 + } + }, + "Limit": { + "type": "integer", + "min": 1, + "max": 100 + }, + "ListDiscoverersOutput": { + "type": "structure", + "members": { + "Discoverers": { + "shape": "__listOfDiscovererSummary", + "documentation": "

    An array of DiscovererSummary information.

    " + }, + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + } + } + }, + "ListDiscoverersRequest": { + "type": "structure", + "members": { + "DiscovererIdPrefix": { + "shape": "__string", + "location": "querystring", + "locationName": "discovererIdPrefix" + }, + "Limit": { + "shape": "__integer", + "location": "querystring", + "locationName": "limit" + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken" + }, + "SourceArnPrefix": { + "shape": "__string", + "location": "querystring", + "locationName": "sourceArnPrefix" + } + } + }, + "ListDiscoverersResponse": { + "type": "structure", + "members": { + "Discoverers": { + "shape": "__listOfDiscovererSummary", + "documentation": "

    An array of DiscovererSummary information.

    " + }, + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + } + } + }, + "ListRegistriesOutput": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "Registries": { + "shape": "__listOfRegistrySummary", + "documentation": "

    An array of registry summaries.

    " + } + }, + "documentation": "

    List the registries.

    " + }, + "ListRegistriesRequest": { + "type": "structure", + "members": { + "Limit": { + "shape": "__integer", + "location": "querystring", + "locationName": "limit" + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken" + }, + "RegistryNamePrefix": { + "shape": "__string", + "location": "querystring", + "locationName": "registryNamePrefix" + }, + "Scope": { + "shape": "__string", + "location": "querystring", + "locationName": "scope" + } + } + }, + "ListRegistriesResponse": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "Registries": { + "shape": "__listOfRegistrySummary", + "documentation": "

    An array of registry summaries.

    " + } + } + }, + "ListSchemaVersionsOutput": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "SchemaVersions": { + "shape": "__listOfSchemaVersionSummary", + "documentation": "

    An array of schema version summaries.

    " + } + } + }, + "ListSchemaVersionsRequest": { + "type": "structure", + "members": { + "Limit": { + "shape": "__integer", + "location": "querystring", + "locationName": "limit" + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken" + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + } + }, + "required": [ + "RegistryName", + "SchemaName" + ] + }, + "ListSchemaVersionsResponse": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "SchemaVersions": { + "shape": "__listOfSchemaVersionSummary", + "documentation": "

    An array of schema version summaries.

    " + } + } + }, + "ListSchemasOutput": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "Schemas": { + "shape": "__listOfSchemaSummary", + "documentation": "

    An array of schema summaries.

    " + } + } + }, + "ListSchemasRequest": { + "type": "structure", + "members": { + "Limit": { + "shape": "__integer", + "location": "querystring", + "locationName": "limit" + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken" + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaNamePrefix": { + "shape": "__string", + "location": "querystring", + "locationName": "schemaNamePrefix" + } + }, + "required": [ + "RegistryName" + ] + }, + "ListSchemasResponse": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "Schemas": { + "shape": "__listOfSchemaSummary", + "documentation": "

    An array of schema summaries.

    " + } + } + }, + "ListTagsForResourceRequest": { + "type": "structure", + "members": { + "ResourceArn": { + "shape": "__string", + "location": "uri", + "locationName": "resource-arn" + } + }, + "required": [ + "ResourceArn" + ] + }, + "ListTagsForResourceResponse": { + "type": "structure", + "members": { + "Tags": { + "shape": "Tags" + } + }, + "required": [ + "Tags" + ] + }, + "LockServiceLinkedRoleInput": { + "type": "structure", + "members": { + "RoleArn": { + "shape": "__stringMin1Max1600" + }, + "Timeout": { + "shape": "__integerMin1Max29000" + } + }, + "required": [ + "Timeout", + "RoleArn" + ] + }, + "LockServiceLinkedRoleOutput": { + "type": "structure", + "members": { + "CanBeDeleted": { + "shape": "__boolean" + }, + "ReasonOfFailure": { + "shape": "__stringMin1Max1600" + }, + "RelatedResources": { + "shape": "__listOfDiscovererSummary" + } + } + }, + "LockServiceLinkedRoleRequest": { + "type": "structure", + "members": { + "RoleArn": { + "shape": "__stringMin1Max1600" + }, + "Timeout": { + "shape": "__integerMin1Max29000" + } + }, + "required": [ + "Timeout", + "RoleArn" + ] + }, + "LockServiceLinkedRoleResponse": { + "type": "structure", + "members": { + "CanBeDeleted": { + "shape": "__boolean" + }, + "ReasonOfFailure": { + "shape": "__stringMin1Max1600" + }, + "RelatedResources": { + "shape": "__listOfDiscovererSummary" + } + } + }, + "NotFoundException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 404 + } + }, + "PutCodeBindingRequest": { + "type": "structure", + "members": { + "Language": { + "shape": "__string", + "location": "uri", + "locationName": "language" + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "SchemaVersion": { + "shape": "__string", + "location": "querystring", + "locationName": "schemaVersion" + } + }, + "required": [ + "RegistryName", + "SchemaName", + "Language" + ] + }, + "PutCodeBindingResponse": { + "type": "structure", + "members": { + "CreationDate": { + "shape": "__timestampIso8601", + "documentation": "

    The time and date that the code binding was created.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that code bindings were modified.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema.

    " + }, + "Status": { + "shape": "CodeGenerationStatus", + "documentation": "

    The current status of code binding generation.

    " + } + } + }, + "RegistryOutput": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the registry.

    " + }, + "RegistryArn": { + "shape": "__string", + "documentation": "

    The ARN of the registry.

    " + }, + "RegistryName": { + "shape": "__string", + "documentation": "

    The name of the registry.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the registry.

    " + } + } + }, + "RegistrySummary": { + "type": "structure", + "members": { + "RegistryArn": { + "shape": "__string", + "documentation": "

    The ARN of the registry.

    " + }, + "RegistryName": { + "shape": "__string", + "documentation": "

    The name of the registry.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the registry.

    " + } + } + }, + "SchemaOutput": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the schema.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that schema was modified.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags" + }, + "Type": { + "shape": "__string", + "documentation": "

    The type of the schema.

    " + }, + "VersionCreatedDate": { + "shape": "__timestampIso8601", + "documentation": "

    The date the schema version was created.

    " + } + } + }, + "SchemaSummary": { + "type": "structure", + "members": { + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that schema was modified.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the schema.

    " + }, + "VersionCount": { + "shape": "__long", + "documentation": "

    The number of versions available for the schema.

    " + } + }, + "documentation": "

    A summary of schema details.

    " + }, + "SchemaVersionSummary": { + "type": "structure", + "members": { + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema version.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema.

    " + } + } + }, + "SearchSchemaSummary": { + "type": "structure", + "members": { + "RegistryName": { + "shape": "__string", + "documentation": "

    The name of the registry.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersions": { + "shape": "__listOfSearchSchemaVersionSummary", + "documentation": "

    An array of schema version summaries.

    " + } + } + }, + "SearchSchemaVersionSummary": { + "type": "structure", + "members": { + "CreatedDate": { + "shape": "__timestampIso8601" + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema

    " + } + } + }, + "SearchSchemasOutput": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "Schemas": { + "shape": "__listOfSearchSchemaSummary", + "documentation": "

    An array of SearchSchemaSummary information.

    " + } + } + }, + "SearchSchemasRequest": { + "type": "structure", + "members": { + "Keywords": { + "shape": "__string", + "location": "querystring", + "locationName": "keywords" + }, + "Limit": { + "shape": "__integer", + "location": "querystring", + "locationName": "limit" + }, + "NextToken": { + "shape": "__string", + "location": "querystring", + "locationName": "nextToken" + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + } + }, + "required": [ + "RegistryName", + "Keywords" + ] + }, + "SearchSchemasResponse": { + "type": "structure", + "members": { + "NextToken": { + "shape": "__string", + "documentation": "

    The token that specifies the next page of results to return. To request the first page, leave NextToken empty. The token will expire in 24 hours, and cannot be shared with other accounts.

    " + }, + "Schemas": { + "shape": "__listOfSearchSchemaSummary", + "documentation": "

    An array of SearchSchemaSummary information.

    " + } + } + }, + "ServiceUnavailableException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 503 + } + }, + "StartDiscovererRequest": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "location": "uri", + "locationName": "discovererId" + } + }, + "required": [ + "DiscovererId" + ] + }, + "StartDiscovererResponse": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + } + } + }, + "StopDiscovererRequest": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "location": "uri", + "locationName": "discovererId" + } + }, + "required": [ + "DiscovererId" + ] + }, + "StopDiscovererResponse": { + "type": "structure", + "members": { + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + } + } + }, + "TagResourceInput": { + "type": "structure", + "members": { + "Tags": { + "shape": "Tags", + "locationName": "tags" + } + }, + "required": [ + "Tags" + ] + }, + "TagResourceRequest": { + "type": "structure", + "members": { + "ResourceArn": { + "shape": "__string", + "location": "uri", + "locationName": "resource-arn" + }, + "Tags": { + "shape": "Tags", + "locationName": "tags" + } + }, + "required": [ + "ResourceArn", + "Tags" + ] + }, + "Tags": { + "type": "map", + "documentation": "

    Key-value pairs associated with a resource.

    ", + "key": { + "shape": "__string" + }, + "value": { + "shape": "__string" + } + }, + "TooManyRequestsException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 429 + } + }, + "Type": { + "type": "string", + "enum": [ + "OpenApi3" + ] + }, + "UnauthorizedException": { + "type": "structure", + "members": { + "Code": { + "shape": "__string", + "documentation": "

    The error code.

    " + }, + "Message": { + "shape": "__string", + "documentation": "

    The message string of the error output.

    " + } + }, + "required": [ + "Message", + "Code" + ], + "exception": true, + "error": { + "httpStatusCode": 401 + } + }, + "UnlockServiceLinkedRoleInput": { + "type": "structure", + "members": { + "RoleArn": { + "shape": "__stringMin1Max1600" + } + }, + "required": [ + "RoleArn" + ] + }, + "UnlockServiceLinkedRoleRequest": { + "type": "structure", + "members": { + "RoleArn": { + "shape": "__stringMin1Max1600" + } + }, + "required": [ + "RoleArn" + ] + }, + "UnlockServiceLinkedRoleResponse": { + "type": "structure", + "members": {} + }, + "UntagResourceRequest": { + "type": "structure", + "members": { + "ResourceArn": { + "shape": "__string", + "location": "uri", + "locationName": "resource-arn" + }, + "TagKeys": { + "shape": "__listOf__string", + "location": "querystring", + "locationName": "tagKeys" + } + }, + "required": [ + "TagKeys", + "ResourceArn" + ] + }, + "UpdateDiscovererInput": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    The description of the discoverer to update.

    " + } + } + }, + "UpdateDiscovererRequest": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    The description of the discoverer to update.

    " + }, + "DiscovererId": { + "shape": "__string", + "location": "uri", + "locationName": "discovererId" + } + }, + "required": [ + "DiscovererId" + ] + }, + "UpdateDiscovererResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the discoverer.

    " + }, + "DiscovererArn": { + "shape": "__string", + "documentation": "

    The ARN of the discoverer.

    " + }, + "DiscovererId": { + "shape": "__string", + "documentation": "

    The ID of the discoverer.

    " + }, + "SourceArn": { + "shape": "__string", + "documentation": "

    The ARN of the event bus.

    " + }, + "State": { + "shape": "DiscovererState", + "documentation": "

    The state of the discoverer.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the resource.

    " + } + } + }, + "UpdateRegistryInput": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    The description of the registry to update.

    " + } + } + }, + "UpdateRegistryRequest": { + "type": "structure", + "members": { + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    The description of the registry to update.

    " + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + } + }, + "required": [ + "RegistryName" + ] + }, + "UpdateRegistryResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the registry.

    " + }, + "RegistryArn": { + "shape": "__string", + "documentation": "

    The ARN of the registry.

    " + }, + "RegistryName": { + "shape": "__string", + "documentation": "

    The name of the registry.

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags", + "documentation": "

    Tags associated with the registry.

    " + } + } + }, + "UpdateSchemaInput": { + "type": "structure", + "members": { + "ClientTokenId": { + "shape": "__stringMin0Max36", + "documentation": "

    The ID of the client token.

    ", + "idempotencyToken": true + }, + "Content": { + "shape": "__stringMin1Max100000", + "documentation": "

    The source of the schema definition.

    " + }, + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    The description of the schema.

    " + }, + "Type": { + "shape": "Type", + "documentation": "

    The schema type for the events schema.

    " + } + } + }, + "UpdateSchemaRequest": { + "type": "structure", + "members": { + "ClientTokenId": { + "shape": "__stringMin0Max36", + "documentation": "

    The ID of the client token.

    ", + "idempotencyToken": true + }, + "Content": { + "shape": "__stringMin1Max100000", + "documentation": "

    The source of the schema definition.

    " + }, + "Description": { + "shape": "__stringMin0Max256", + "documentation": "

    The description of the schema.

    " + }, + "RegistryName": { + "shape": "__string", + "location": "uri", + "locationName": "registryName" + }, + "SchemaName": { + "shape": "__string", + "location": "uri", + "locationName": "schemaName" + }, + "Type": { + "shape": "Type", + "documentation": "

    The schema type for the events schema.

    " + } + }, + "required": [ + "RegistryName", + "SchemaName" + ] + }, + "UpdateSchemaResponse": { + "type": "structure", + "members": { + "Description": { + "shape": "__string", + "documentation": "

    The description of the schema.

    " + }, + "LastModified": { + "shape": "__timestampIso8601", + "documentation": "

    The date and time that schema was modified.

    " + }, + "SchemaArn": { + "shape": "__string", + "documentation": "

    The ARN of the schema.

    " + }, + "SchemaName": { + "shape": "__string", + "documentation": "

    The name of the schema.

    " + }, + "SchemaVersion": { + "shape": "__string", + "documentation": "

    The version number of the schema

    " + }, + "Tags": { + "shape": "Tags", + "locationName": "tags" + }, + "Type": { + "shape": "__string", + "documentation": "

    The type of the schema.

    " + }, + "VersionCreatedDate": { + "shape": "__timestampIso8601", + "documentation": "

    The date the schema version was created.

    " + } + } + }, + "__boolean": { + "type": "boolean" + }, + "__double": { + "type": "double" + }, + "__integer": { + "type": "integer" + }, + "__integerMin1Max29000": { + "type": "integer", + "min": 1, + "max": 29000 + }, + "__listOfDiscovererSummary": { + "type": "list", + "member": { + "shape": "DiscovererSummary" + } + }, + "__listOfGetDiscoveredSchemaVersionItemInput": { + "type": "list", + "min": 1, + "max": 10, + "member": { + "shape": "GetDiscoveredSchemaVersionItemInput" + } + }, + "__listOfRegistrySummary": { + "type": "list", + "member": { + "shape": "RegistrySummary" + } + }, + "__listOfSchemaSummary": { + "type": "list", + "member": { + "shape": "SchemaSummary" + } + }, + "__listOfSchemaVersionSummary": { + "type": "list", + "member": { + "shape": "SchemaVersionSummary" + } + }, + "__listOfSearchSchemaSummary": { + "type": "list", + "member": { + "shape": "SearchSchemaSummary" + } + }, + "__listOfSearchSchemaVersionSummary": { + "type": "list", + "member": { + "shape": "SearchSchemaVersionSummary" + } + }, + "__listOf__string": { + "type": "list", + "member": { + "shape": "__string" + } + }, + "__long": { + "type": "long" + }, + "__string": { + "type": "string" + }, + "__stringMin0Max256": { + "type": "string", + "min": 0, + "max": 256 + }, + "__stringMin0Max36": { + "type": "string", + "min": 0, + "max": 36 + }, + "__stringMin1Max100000": { + "type": "string", + "min": 1, + "max": 100000 + }, + "__stringMin1Max1600": { + "type": "string", + "min": 1, + "max": 1600 + }, + "__stringMin20Max1600": { + "type": "string", + "min": 20, + "max": 1600 + }, + "__timestampIso8601": { + "type": "timestamp", + "timestampFormat": "iso8601" + }, + "__timestampUnix": { + "type": "timestamp", + "timestampFormat": "unixTimestamp" + }, + "Body": { + "type": "blob" + } + }, + "documentation": "

    AWS EventBridge Schemas

    " +} diff --git a/services/schemas/src/main/resources/codegen-resources/waiters-2.json b/services/schemas/src/main/resources/codegen-resources/waiters-2.json new file mode 100644 index 000000000000..4f642f615c44 --- /dev/null +++ b/services/schemas/src/main/resources/codegen-resources/waiters-2.json @@ -0,0 +1,36 @@ +{ + "version": 2, + "waiters": { + "CodeBindingExists": { + "description": "Wait until code binding is generated", + "delay": 2, + "operation": "DescribeCodeBinding", + "maxAttempts": 30, + "acceptors": [ + { + "expected": "CREATE_COMPLETE", + "matcher": "path", + "state": "success", + "argument": "Status" + }, + { + "expected": "CREATE_IN_PROGRESS", + "matcher": "path", + "state": "retry", + "argument": "Status" + }, + { + "expected": "CREATE_FAILED", + "matcher": "path", + "state": "failure", + "argument": "Status" + }, + { + "matcher": "error", + "expected": "NotFoundException", + "state": "failure" + } + ] + } + } +} diff --git a/services/secretsmanager/pom.xml b/services/secretsmanager/pom.xml index 86a4e123bb31..b38acf0441e3 100644 --- a/services/secretsmanager/pom.xml +++ b/services/secretsmanager/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + sesv2 + AWS Java SDK :: Services :: SESv2 + The AWS Java SDK for SESv2 module holds the client classes that are used for + communicating with SESv2. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.sesv2 + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/sesv2/src/main/resources/codegen-resources/paginators-1.json b/services/sesv2/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..c5e4de2551c2 --- /dev/null +++ b/services/sesv2/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,39 @@ +{ + "pagination": { + "GetDedicatedIps": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + }, + "ListConfigurationSets": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + }, + "ListDedicatedIpPools": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + }, + "ListDeliverabilityTestReports": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + }, + "ListDomainDeliverabilityCampaigns": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + }, + "ListEmailIdentities": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + }, + "ListSuppressedDestinations": { + "input_token": "NextToken", + "output_token": "NextToken", + "limit_key": "PageSize" + } + } +} diff --git a/services/sesv2/src/main/resources/codegen-resources/service-2.json b/services/sesv2/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..9a5139e977a0 --- /dev/null +++ b/services/sesv2/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,3467 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-09-27", + "endpointPrefix":"email", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"Amazon SES V2", + "serviceFullName":"Amazon Simple Email Service", + "serviceId":"SESv2", + "signatureVersion":"v4", + "signingName":"ses", + "uid":"sesv2-2019-09-27" + }, + "operations":{ + "CreateConfigurationSet":{ + "name":"CreateConfigurationSet", + "http":{ + "method":"POST", + "requestUri":"/v2/email/configuration-sets" + }, + "input":{"shape":"CreateConfigurationSetRequest"}, + "output":{"shape":"CreateConfigurationSetResponse"}, + "errors":[ + {"shape":"AlreadyExistsException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"LimitExceededException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Create a configuration set. Configuration sets are groups of rules that you can apply to the emails that you send. You apply a configuration set to an email by specifying the name of the configuration set when you call the Amazon SES API v2. When you apply a configuration set to an email, all of the rules in that configuration set are applied to the email.

    " + }, + "CreateConfigurationSetEventDestination":{ + "name":"CreateConfigurationSetEventDestination", + "http":{ + "method":"POST", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/event-destinations" + }, + "input":{"shape":"CreateConfigurationSetEventDestinationRequest"}, + "output":{"shape":"CreateConfigurationSetEventDestinationResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"AlreadyExistsException"}, + {"shape":"LimitExceededException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Create an event destination. Events include message sends, deliveries, opens, clicks, bounces, and complaints. Event destinations are places that you can send information about these events to. For example, you can send event data to Amazon SNS to receive notifications when you receive bounces or complaints, or you can use Amazon Kinesis Data Firehose to stream data to Amazon S3 for long-term storage.

    A single configuration set can include more than one event destination.

    " + }, + "CreateDedicatedIpPool":{ + "name":"CreateDedicatedIpPool", + "http":{ + "method":"POST", + "requestUri":"/v2/email/dedicated-ip-pools" + }, + "input":{"shape":"CreateDedicatedIpPoolRequest"}, + "output":{"shape":"CreateDedicatedIpPoolResponse"}, + "errors":[ + {"shape":"AlreadyExistsException"}, + {"shape":"LimitExceededException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Create a new pool of dedicated IP addresses. A pool can include one or more dedicated IP addresses that are associated with your AWS account. You can associate a pool with a configuration set. When you send an email that uses that configuration set, the message is sent from one of the addresses in the associated pool.

    " + }, + "CreateDeliverabilityTestReport":{ + "name":"CreateDeliverabilityTestReport", + "http":{ + "method":"POST", + "requestUri":"/v2/email/deliverability-dashboard/test" + }, + "input":{"shape":"CreateDeliverabilityTestReportRequest"}, + "output":{"shape":"CreateDeliverabilityTestReportResponse"}, + "errors":[ + {"shape":"AccountSuspendedException"}, + {"shape":"SendingPausedException"}, + {"shape":"MessageRejected"}, + {"shape":"MailFromDomainNotVerifiedException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"LimitExceededException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Create a new predictive inbox placement test. Predictive inbox placement tests can help you predict how your messages will be handled by various email providers around the world. When you perform a predictive inbox placement test, you provide a sample message that contains the content that you plan to send to your customers. Amazon SES then sends that message to special email addresses spread across several major email providers. After about 24 hours, the test is complete, and you can use the GetDeliverabilityTestReport operation to view the results of the test.

    " + }, + "CreateEmailIdentity":{ + "name":"CreateEmailIdentity", + "http":{ + "method":"POST", + "requestUri":"/v2/email/identities" + }, + "input":{"shape":"CreateEmailIdentityRequest"}, + "output":{"shape":"CreateEmailIdentityResponse"}, + "errors":[ + {"shape":"AlreadyExistsException"}, + {"shape":"LimitExceededException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Starts the process of verifying an email identity. An identity is an email address or domain that you use when you send email. Before you can use an identity to send email, you first have to verify it. By verifying an identity, you demonstrate that you're the owner of the identity, and that you've given Amazon SES API v2 permission to send email from the identity.

    When you verify an email address, Amazon SES sends an email to the address. Your email address is verified as soon as you follow the link in the verification email.

    When you verify a domain without specifying the DkimSigningAttributes object, this operation provides a set of DKIM tokens. You can convert these tokens into CNAME records, which you then add to the DNS configuration for your domain. Your domain is verified when Amazon SES detects these records in the DNS configuration for your domain. This verification method is known as Easy DKIM.

    Alternatively, you can perform the verification process by providing your own public-private key pair. This verification method is known as Bring Your Own DKIM (BYODKIM). To use BYODKIM, your call to the CreateEmailIdentity operation has to include the DkimSigningAttributes object. When you specify this object, you provide a selector (a component of the DNS record name that identifies the public key that you want to use for DKIM authentication) and a private key.

    " + }, + "DeleteConfigurationSet":{ + "name":"DeleteConfigurationSet", + "http":{ + "method":"DELETE", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}" + }, + "input":{"shape":"DeleteConfigurationSetRequest"}, + "output":{"shape":"DeleteConfigurationSetResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Delete an existing configuration set.

    Configuration sets are groups of rules that you can apply to the emails you send. You apply a configuration set to an email by including a reference to the configuration set in the headers of the email. When you apply a configuration set to an email, all of the rules in that configuration set are applied to the email.

    " + }, + "DeleteConfigurationSetEventDestination":{ + "name":"DeleteConfigurationSetEventDestination", + "http":{ + "method":"DELETE", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/event-destinations/{EventDestinationName}" + }, + "input":{"shape":"DeleteConfigurationSetEventDestinationRequest"}, + "output":{"shape":"DeleteConfigurationSetEventDestinationResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Delete an event destination.

    Events include message sends, deliveries, opens, clicks, bounces, and complaints. Event destinations are places that you can send information about these events to. For example, you can send event data to Amazon SNS to receive notifications when you receive bounces or complaints, or you can use Amazon Kinesis Data Firehose to stream data to Amazon S3 for long-term storage.

    " + }, + "DeleteDedicatedIpPool":{ + "name":"DeleteDedicatedIpPool", + "http":{ + "method":"DELETE", + "requestUri":"/v2/email/dedicated-ip-pools/{PoolName}" + }, + "input":{"shape":"DeleteDedicatedIpPoolRequest"}, + "output":{"shape":"DeleteDedicatedIpPoolResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Delete a dedicated IP pool.

    " + }, + "DeleteEmailIdentity":{ + "name":"DeleteEmailIdentity", + "http":{ + "method":"DELETE", + "requestUri":"/v2/email/identities/{EmailIdentity}" + }, + "input":{"shape":"DeleteEmailIdentityRequest"}, + "output":{"shape":"DeleteEmailIdentityResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"} + ], + "documentation":"

    Deletes an email identity. An identity can be either an email address or a domain name.

    " + }, + "DeleteSuppressedDestination":{ + "name":"DeleteSuppressedDestination", + "http":{ + "method":"DELETE", + "requestUri":"/v2/email/suppression/addresses/{EmailAddress}" + }, + "input":{"shape":"DeleteSuppressedDestinationRequest"}, + "output":{"shape":"DeleteSuppressedDestinationResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"}, + {"shape":"TooManyRequestsException"} + ], + "documentation":"

    Removes an email address from the suppression list for your account.

    " + }, + "GetAccount":{ + "name":"GetAccount", + "http":{ + "method":"GET", + "requestUri":"/v2/email/account" + }, + "input":{"shape":"GetAccountRequest"}, + "output":{"shape":"GetAccountResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Obtain information about the email-sending status and capabilities of your Amazon SES account in the current AWS Region.

    " + }, + "GetBlacklistReports":{ + "name":"GetBlacklistReports", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard/blacklist-report" + }, + "input":{"shape":"GetBlacklistReportsRequest"}, + "output":{"shape":"GetBlacklistReportsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve a list of the blacklists that your dedicated IP addresses appear on.

    " + }, + "GetConfigurationSet":{ + "name":"GetConfigurationSet", + "http":{ + "method":"GET", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}" + }, + "input":{"shape":"GetConfigurationSetRequest"}, + "output":{"shape":"GetConfigurationSetResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Get information about an existing configuration set, including the dedicated IP pool that it's associated with, whether or not it's enabled for sending email, and more.

    Configuration sets are groups of rules that you can apply to the emails you send. You apply a configuration set to an email by including a reference to the configuration set in the headers of the email. When you apply a configuration set to an email, all of the rules in that configuration set are applied to the email.

    " + }, + "GetConfigurationSetEventDestinations":{ + "name":"GetConfigurationSetEventDestinations", + "http":{ + "method":"GET", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/event-destinations" + }, + "input":{"shape":"GetConfigurationSetEventDestinationsRequest"}, + "output":{"shape":"GetConfigurationSetEventDestinationsResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve a list of event destinations that are associated with a configuration set.

    Events include message sends, deliveries, opens, clicks, bounces, and complaints. Event destinations are places that you can send information about these events to. For example, you can send event data to Amazon SNS to receive notifications when you receive bounces or complaints, or you can use Amazon Kinesis Data Firehose to stream data to Amazon S3 for long-term storage.

    " + }, + "GetDedicatedIp":{ + "name":"GetDedicatedIp", + "http":{ + "method":"GET", + "requestUri":"/v2/email/dedicated-ips/{IP}" + }, + "input":{"shape":"GetDedicatedIpRequest"}, + "output":{"shape":"GetDedicatedIpResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Get information about a dedicated IP address, including the name of the dedicated IP pool that it's associated with, as well information about the automatic warm-up process for the address.

    " + }, + "GetDedicatedIps":{ + "name":"GetDedicatedIps", + "http":{ + "method":"GET", + "requestUri":"/v2/email/dedicated-ips" + }, + "input":{"shape":"GetDedicatedIpsRequest"}, + "output":{"shape":"GetDedicatedIpsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    List the dedicated IP addresses that are associated with your AWS account.

    " + }, + "GetDeliverabilityDashboardOptions":{ + "name":"GetDeliverabilityDashboardOptions", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard" + }, + "input":{"shape":"GetDeliverabilityDashboardOptionsRequest"}, + "output":{"shape":"GetDeliverabilityDashboardOptionsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"LimitExceededException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve information about the status of the Deliverability dashboard for your account. When the Deliverability dashboard is enabled, you gain access to reputation, deliverability, and other metrics for the domains that you use to send email. You also gain the ability to perform predictive inbox placement tests.

    When you use the Deliverability dashboard, you pay a monthly subscription charge, in addition to any other fees that you accrue by using Amazon SES and other AWS services. For more information about the features and cost of a Deliverability dashboard subscription, see Amazon SES Pricing.

    " + }, + "GetDeliverabilityTestReport":{ + "name":"GetDeliverabilityTestReport", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard/test-reports/{ReportId}" + }, + "input":{"shape":"GetDeliverabilityTestReportRequest"}, + "output":{"shape":"GetDeliverabilityTestReportResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve the results of a predictive inbox placement test.

    " + }, + "GetDomainDeliverabilityCampaign":{ + "name":"GetDomainDeliverabilityCampaign", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard/campaigns/{CampaignId}" + }, + "input":{"shape":"GetDomainDeliverabilityCampaignRequest"}, + "output":{"shape":"GetDomainDeliverabilityCampaignResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"NotFoundException"} + ], + "documentation":"

    Retrieve all the deliverability data for a specific campaign. This data is available for a campaign only if the campaign sent email by using a domain that the Deliverability dashboard is enabled for.

    " + }, + "GetDomainStatisticsReport":{ + "name":"GetDomainStatisticsReport", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard/statistics-report/{Domain}" + }, + "input":{"shape":"GetDomainStatisticsReportRequest"}, + "output":{"shape":"GetDomainStatisticsReportResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Retrieve inbox placement and engagement rates for the domains that you use to send email.

    " + }, + "GetEmailIdentity":{ + "name":"GetEmailIdentity", + "http":{ + "method":"GET", + "requestUri":"/v2/email/identities/{EmailIdentity}" + }, + "input":{"shape":"GetEmailIdentityRequest"}, + "output":{"shape":"GetEmailIdentityResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Provides information about a specific identity, including the identity's verification status, its DKIM authentication status, and its custom Mail-From settings.

    " + }, + "GetSuppressedDestination":{ + "name":"GetSuppressedDestination", + "http":{ + "method":"GET", + "requestUri":"/v2/email/suppression/addresses/{EmailAddress}" + }, + "input":{"shape":"GetSuppressedDestinationRequest"}, + "output":{"shape":"GetSuppressedDestinationResponse"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"} + ], + "documentation":"

    Retrieves information about a specific email address that's on the suppression list for your account.

    " + }, + "ListConfigurationSets":{ + "name":"ListConfigurationSets", + "http":{ + "method":"GET", + "requestUri":"/v2/email/configuration-sets" + }, + "input":{"shape":"ListConfigurationSetsRequest"}, + "output":{"shape":"ListConfigurationSetsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    List all of the configuration sets associated with your account in the current region.

    Configuration sets are groups of rules that you can apply to the emails you send. You apply a configuration set to an email by including a reference to the configuration set in the headers of the email. When you apply a configuration set to an email, all of the rules in that configuration set are applied to the email.

    " + }, + "ListDedicatedIpPools":{ + "name":"ListDedicatedIpPools", + "http":{ + "method":"GET", + "requestUri":"/v2/email/dedicated-ip-pools" + }, + "input":{"shape":"ListDedicatedIpPoolsRequest"}, + "output":{"shape":"ListDedicatedIpPoolsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    List all of the dedicated IP pools that exist in your AWS account in the current Region.

    " + }, + "ListDeliverabilityTestReports":{ + "name":"ListDeliverabilityTestReports", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard/test-reports" + }, + "input":{"shape":"ListDeliverabilityTestReportsRequest"}, + "output":{"shape":"ListDeliverabilityTestReportsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Show a list of the predictive inbox placement tests that you've performed, regardless of their statuses. For predictive inbox placement tests that are complete, you can use the GetDeliverabilityTestReport operation to view the results.

    " + }, + "ListDomainDeliverabilityCampaigns":{ + "name":"ListDomainDeliverabilityCampaigns", + "http":{ + "method":"GET", + "requestUri":"/v2/email/deliverability-dashboard/domains/{SubscribedDomain}/campaigns" + }, + "input":{"shape":"ListDomainDeliverabilityCampaignsRequest"}, + "output":{"shape":"ListDomainDeliverabilityCampaignsResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"}, + {"shape":"NotFoundException"} + ], + "documentation":"

    Retrieve deliverability data for all the campaigns that used a specific domain to send email during a specified time range. This data is available for a domain only if you enabled the Deliverability dashboard for the domain.

    " + }, + "ListEmailIdentities":{ + "name":"ListEmailIdentities", + "http":{ + "method":"GET", + "requestUri":"/v2/email/identities" + }, + "input":{"shape":"ListEmailIdentitiesRequest"}, + "output":{"shape":"ListEmailIdentitiesResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Returns a list of all of the email identities that are associated with your AWS account. An identity can be either an email address or a domain. This operation returns identities that are verified as well as those that aren't. This operation returns identities that are associated with Amazon SES and Amazon Pinpoint.

    " + }, + "ListSuppressedDestinations":{ + "name":"ListSuppressedDestinations", + "http":{ + "method":"GET", + "requestUri":"/v2/email/suppression/addresses" + }, + "input":{"shape":"ListSuppressedDestinationsRequest"}, + "output":{"shape":"ListSuppressedDestinationsResponse"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"InvalidNextTokenException"} + ], + "documentation":"

    Retrieves a list of email addresses that are on the suppression list for your account.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"GET", + "requestUri":"/v2/email/tags" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"} + ], + "documentation":"

    Retrieve a list of the tags (keys and values) that are associated with a specified resource. A tag is a label that you optionally define and associate with a resource. Each tag consists of a required tag key and an optional associated tag value. A tag key is a general label that acts as a category for more specific tag values. A tag value acts as a descriptor within a tag key.

    " + }, + "PutAccountDedicatedIpWarmupAttributes":{ + "name":"PutAccountDedicatedIpWarmupAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/account/dedicated-ips/warmup" + }, + "input":{"shape":"PutAccountDedicatedIpWarmupAttributesRequest"}, + "output":{"shape":"PutAccountDedicatedIpWarmupAttributesResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Enable or disable the automatic warm-up feature for dedicated IP addresses.

    " + }, + "PutAccountSendingAttributes":{ + "name":"PutAccountSendingAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/account/sending" + }, + "input":{"shape":"PutAccountSendingAttributesRequest"}, + "output":{"shape":"PutAccountSendingAttributesResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Enable or disable the ability of your account to send email.

    " + }, + "PutAccountSuppressionAttributes":{ + "name":"PutAccountSuppressionAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/account/suppression" + }, + "input":{"shape":"PutAccountSuppressionAttributesRequest"}, + "output":{"shape":"PutAccountSuppressionAttributesResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Change the settings for the account-level suppression list.

    " + }, + "PutConfigurationSetDeliveryOptions":{ + "name":"PutConfigurationSetDeliveryOptions", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/delivery-options" + }, + "input":{"shape":"PutConfigurationSetDeliveryOptionsRequest"}, + "output":{"shape":"PutConfigurationSetDeliveryOptionsResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Associate a configuration set with a dedicated IP pool. You can use dedicated IP pools to create groups of dedicated IP addresses for sending specific types of email.

    " + }, + "PutConfigurationSetReputationOptions":{ + "name":"PutConfigurationSetReputationOptions", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/reputation-options" + }, + "input":{"shape":"PutConfigurationSetReputationOptionsRequest"}, + "output":{"shape":"PutConfigurationSetReputationOptionsResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Enable or disable collection of reputation metrics for emails that you send using a particular configuration set in a specific AWS Region.

    " + }, + "PutConfigurationSetSendingOptions":{ + "name":"PutConfigurationSetSendingOptions", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/sending" + }, + "input":{"shape":"PutConfigurationSetSendingOptionsRequest"}, + "output":{"shape":"PutConfigurationSetSendingOptionsResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Enable or disable email sending for messages that use a particular configuration set in a specific AWS Region.

    " + }, + "PutConfigurationSetSuppressionOptions":{ + "name":"PutConfigurationSetSuppressionOptions", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/suppression-options" + }, + "input":{"shape":"PutConfigurationSetSuppressionOptionsRequest"}, + "output":{"shape":"PutConfigurationSetSuppressionOptionsResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Specify the account suppression list preferences for a configuration set.

    " + }, + "PutConfigurationSetTrackingOptions":{ + "name":"PutConfigurationSetTrackingOptions", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/tracking-options" + }, + "input":{"shape":"PutConfigurationSetTrackingOptionsRequest"}, + "output":{"shape":"PutConfigurationSetTrackingOptionsResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Specify a custom domain to use for open and click tracking elements in email that you send.

    " + }, + "PutDedicatedIpInPool":{ + "name":"PutDedicatedIpInPool", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/dedicated-ips/{IP}/pool" + }, + "input":{"shape":"PutDedicatedIpInPoolRequest"}, + "output":{"shape":"PutDedicatedIpInPoolResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Move a dedicated IP address to an existing dedicated IP pool.

    The dedicated IP address that you specify must already exist, and must be associated with your AWS account.

    The dedicated IP pool you specify must already exist. You can create a new pool by using the CreateDedicatedIpPool operation.

    " + }, + "PutDedicatedIpWarmupAttributes":{ + "name":"PutDedicatedIpWarmupAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/dedicated-ips/{IP}/warmup" + }, + "input":{"shape":"PutDedicatedIpWarmupAttributesRequest"}, + "output":{"shape":"PutDedicatedIpWarmupAttributesResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    " + }, + "PutDeliverabilityDashboardOption":{ + "name":"PutDeliverabilityDashboardOption", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/deliverability-dashboard" + }, + "input":{"shape":"PutDeliverabilityDashboardOptionRequest"}, + "output":{"shape":"PutDeliverabilityDashboardOptionResponse"}, + "errors":[ + {"shape":"AlreadyExistsException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"LimitExceededException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Enable or disable the Deliverability dashboard. When you enable the Deliverability dashboard, you gain access to reputation, deliverability, and other metrics for the domains that you use to send email. You also gain the ability to perform predictive inbox placement tests.

    When you use the Deliverability dashboard, you pay a monthly subscription charge, in addition to any other fees that you accrue by using Amazon SES and other AWS services. For more information about the features and cost of a Deliverability dashboard subscription, see Amazon SES Pricing.

    " + }, + "PutEmailIdentityDkimAttributes":{ + "name":"PutEmailIdentityDkimAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/identities/{EmailIdentity}/dkim" + }, + "input":{"shape":"PutEmailIdentityDkimAttributesRequest"}, + "output":{"shape":"PutEmailIdentityDkimAttributesResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Used to enable or disable DKIM authentication for an email identity.

    " + }, + "PutEmailIdentityDkimSigningAttributes":{ + "name":"PutEmailIdentityDkimSigningAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v1/email/identities/{EmailIdentity}/dkim/signing" + }, + "input":{"shape":"PutEmailIdentityDkimSigningAttributesRequest"}, + "output":{"shape":"PutEmailIdentityDkimSigningAttributesResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Used to configure or change the DKIM authentication settings for an email domain identity. You can use this operation to do any of the following:

    • Update the signing attributes for an identity that uses Bring Your Own DKIM (BYODKIM).

    • Change from using no DKIM authentication to using Easy DKIM.

    • Change from using no DKIM authentication to using BYODKIM.

    • Change from using Easy DKIM to using BYODKIM.

    • Change from using BYODKIM to using Easy DKIM.

    " + }, + "PutEmailIdentityFeedbackAttributes":{ + "name":"PutEmailIdentityFeedbackAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/identities/{EmailIdentity}/feedback" + }, + "input":{"shape":"PutEmailIdentityFeedbackAttributesRequest"}, + "output":{"shape":"PutEmailIdentityFeedbackAttributesResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Used to enable or disable feedback forwarding for an identity. This setting determines what happens when an identity is used to send an email that results in a bounce or complaint event.

    If the value is true, you receive email notifications when bounce or complaint events occur. These notifications are sent to the address that you specified in the Return-Path header of the original email.

    You're required to have a method of tracking bounces and complaints. If you haven't set up another mechanism for receiving bounce or complaint notifications (for example, by setting up an event destination), you receive an email notification when these events occur (even if this setting is disabled).

    " + }, + "PutEmailIdentityMailFromAttributes":{ + "name":"PutEmailIdentityMailFromAttributes", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/identities/{EmailIdentity}/mail-from" + }, + "input":{"shape":"PutEmailIdentityMailFromAttributesRequest"}, + "output":{"shape":"PutEmailIdentityMailFromAttributesResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Used to enable or disable the custom Mail-From domain configuration for an email identity.

    " + }, + "PutSuppressedDestination":{ + "name":"PutSuppressedDestination", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/suppression/addresses" + }, + "input":{"shape":"PutSuppressedDestinationRequest"}, + "output":{"shape":"PutSuppressedDestinationResponse"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"TooManyRequestsException"} + ], + "documentation":"

    Adds an email address to the suppression list for your account.

    " + }, + "SendEmail":{ + "name":"SendEmail", + "http":{ + "method":"POST", + "requestUri":"/v2/email/outbound-emails" + }, + "input":{"shape":"SendEmailRequest"}, + "output":{"shape":"SendEmailResponse"}, + "errors":[ + {"shape":"TooManyRequestsException"}, + {"shape":"LimitExceededException"}, + {"shape":"AccountSuspendedException"}, + {"shape":"SendingPausedException"}, + {"shape":"MessageRejected"}, + {"shape":"MailFromDomainNotVerifiedException"}, + {"shape":"NotFoundException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Sends an email message. You can use the Amazon SES API v2 to send two types of messages:

    • Simple – A standard email message. When you create this type of message, you specify the sender, the recipient, and the message body, and Amazon SES assembles the message for you.

    • Raw – A raw, MIME-formatted email message. When you send this type of email, you have to specify all of the message headers, as well as the message body. You can use this message type to send messages that contain attachments. The message that you specify has to be a valid MIME message.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/v2/email/tags" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"} + ], + "documentation":"

    Add one or more tags (keys and values) to a specified resource. A tag is a label that you optionally define and associate with a resource. Tags can help you categorize and manage resources in different ways, such as by purpose, owner, environment, or other criteria. A resource can have as many as 50 tags.

    Each tag consists of a required tag key and an associated tag value, both of which you define. A tag key is a general label that acts as a category for more specific tag values. A tag value acts as a descriptor within a tag key.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"DELETE", + "requestUri":"/v2/email/tags" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"BadRequestException"}, + {"shape":"ConcurrentModificationException"}, + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"} + ], + "documentation":"

    Remove one or more tags (keys and values) from a specified resource.

    " + }, + "UpdateConfigurationSetEventDestination":{ + "name":"UpdateConfigurationSetEventDestination", + "http":{ + "method":"PUT", + "requestUri":"/v2/email/configuration-sets/{ConfigurationSetName}/event-destinations/{EventDestinationName}" + }, + "input":{"shape":"UpdateConfigurationSetEventDestinationRequest"}, + "output":{"shape":"UpdateConfigurationSetEventDestinationResponse"}, + "errors":[ + {"shape":"NotFoundException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"BadRequestException"} + ], + "documentation":"

    Update the configuration of an event destination for a configuration set.

    Events include message sends, deliveries, opens, clicks, bounces, and complaints. Event destinations are places that you can send information about these events to. For example, you can send event data to Amazon SNS to receive notifications when you receive bounces or complaints, or you can use Amazon Kinesis Data Firehose to stream data to Amazon S3 for long-term storage.

    " + } + }, + "shapes":{ + "AccountSuspendedException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The message can't be sent because the account's ability to send email has been permanently restricted.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "AlreadyExistsException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The resource specified in your request already exists.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "AmazonResourceName":{"type":"string"}, + "BadRequestException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The input you provided is invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "BehaviorOnMxFailure":{ + "type":"string", + "documentation":"

    The action that you want to take if the required MX record can't be found when you send an email. When you set this value to UseDefaultValue, the mail is sent using amazonses.com as the MAIL FROM domain. When you set this value to RejectMessage, the Amazon SES API v2 returns a MailFromDomainNotVerified error, and doesn't attempt to deliver the email.

    These behaviors are taken when the custom MAIL FROM domain configuration is in the Pending, Failed, and TemporaryFailure states.

    ", + "enum":[ + "USE_DEFAULT_VALUE", + "REJECT_MESSAGE" + ] + }, + "BlacklistEntries":{ + "type":"list", + "member":{"shape":"BlacklistEntry"} + }, + "BlacklistEntry":{ + "type":"structure", + "members":{ + "RblName":{ + "shape":"RblName", + "documentation":"

    The name of the blacklist that the IP address appears on.

    " + }, + "ListingTime":{ + "shape":"Timestamp", + "documentation":"

    The time when the blacklisting event occurred, shown in Unix time format.

    " + }, + "Description":{ + "shape":"BlacklistingDescription", + "documentation":"

    Additional information about the blacklisting event, as provided by the blacklist maintainer.

    " + } + }, + "documentation":"

    An object that contains information about a blacklisting event that impacts one of the dedicated IP addresses that is associated with your account.

    " + }, + "BlacklistItemName":{ + "type":"string", + "documentation":"

    An IP address that you want to obtain blacklist information for.

    " + }, + "BlacklistItemNames":{ + "type":"list", + "member":{"shape":"BlacklistItemName"} + }, + "BlacklistReport":{ + "type":"map", + "key":{"shape":"BlacklistItemName"}, + "value":{"shape":"BlacklistEntries"} + }, + "BlacklistingDescription":{ + "type":"string", + "documentation":"

    A description of the blacklisting event.

    " + }, + "Body":{ + "type":"structure", + "members":{ + "Text":{ + "shape":"Content", + "documentation":"

    An object that represents the version of the message that is displayed in email clients that don't support HTML, or clients where the recipient has disabled HTML rendering.

    " + }, + "Html":{ + "shape":"Content", + "documentation":"

    An object that represents the version of the message that is displayed in email clients that support HTML. HTML messages can include formatted text, hyperlinks, images, and more.

    " + } + }, + "documentation":"

    Represents the body of the email message.

    " + }, + "CampaignId":{"type":"string"}, + "Charset":{"type":"string"}, + "CloudWatchDestination":{ + "type":"structure", + "required":["DimensionConfigurations"], + "members":{ + "DimensionConfigurations":{ + "shape":"CloudWatchDimensionConfigurations", + "documentation":"

    An array of objects that define the dimensions to use when you send email events to Amazon CloudWatch.

    " + } + }, + "documentation":"

    An object that defines an Amazon CloudWatch destination for email events. You can use Amazon CloudWatch to monitor and gain insights on your email sending metrics.

    " + }, + "CloudWatchDimensionConfiguration":{ + "type":"structure", + "required":[ + "DimensionName", + "DimensionValueSource", + "DefaultDimensionValue" + ], + "members":{ + "DimensionName":{ + "shape":"DimensionName", + "documentation":"

    The name of an Amazon CloudWatch dimension associated with an email sending metric. The name has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + }, + "DimensionValueSource":{ + "shape":"DimensionValueSource", + "documentation":"

    The location where the Amazon SES API v2 finds the value of a dimension to publish to Amazon CloudWatch. If you want to use the message tags that you specify using an X-SES-MESSAGE-TAGS header or a parameter to the SendEmail or SendRawEmail API, choose messageTag. If you want to use your own email headers, choose emailHeader. If you want to use link tags, choose linkTags.

    " + }, + "DefaultDimensionValue":{ + "shape":"DefaultDimensionValue", + "documentation":"

    The default value of the dimension that is published to Amazon CloudWatch if you don't provide the value of the dimension when you send an email. This value has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + } + }, + "documentation":"

    An object that defines the dimension configuration to use when you send email events to Amazon CloudWatch.

    " + }, + "CloudWatchDimensionConfigurations":{ + "type":"list", + "member":{"shape":"CloudWatchDimensionConfiguration"} + }, + "ConcurrentModificationException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The resource is being modified by another operation or thread.

    ", + "error":{"httpStatusCode":500}, + "exception":true + }, + "ConfigurationSetName":{ + "type":"string", + "documentation":"

    The name of a configuration set.

    Configuration sets are groups of rules that you can apply to the emails you send. You apply a configuration set to an email by including a reference to the configuration set in the headers of the email. When you apply a configuration set to an email, all of the rules in that configuration set are applied to the email.

    " + }, + "ConfigurationSetNameList":{ + "type":"list", + "member":{"shape":"ConfigurationSetName"} + }, + "Content":{ + "type":"structure", + "required":["Data"], + "members":{ + "Data":{ + "shape":"MessageData", + "documentation":"

    The content of the message itself.

    " + }, + "Charset":{ + "shape":"Charset", + "documentation":"

    The character set for the content. Because of the constraints of the SMTP protocol, Amazon SES uses 7-bit ASCII by default. If the text includes characters outside of the ASCII range, you have to specify a character set. For example, you could specify UTF-8, ISO-8859-1, or Shift_JIS.

    " + } + }, + "documentation":"

    An object that represents the content of the email, and optionally a character set specification.

    " + }, + "CreateConfigurationSetEventDestinationRequest":{ + "type":"structure", + "required":[ + "ConfigurationSetName", + "EventDestinationName", + "EventDestination" + ], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to add an event destination to.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "EventDestinationName":{ + "shape":"EventDestinationName", + "documentation":"

    A name that identifies the event destination within the configuration set.

    " + }, + "EventDestination":{ + "shape":"EventDestinationDefinition", + "documentation":"

    An object that defines the event destination.

    " + } + }, + "documentation":"

    A request to add an event destination to a configuration set.

    " + }, + "CreateConfigurationSetEventDestinationResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "CreateConfigurationSetRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set.

    " + }, + "TrackingOptions":{ + "shape":"TrackingOptions", + "documentation":"

    An object that defines the open and click tracking options for emails that you send using the configuration set.

    " + }, + "DeliveryOptions":{ + "shape":"DeliveryOptions", + "documentation":"

    An object that defines the dedicated IP pool that is used to send emails that you send using the configuration set.

    " + }, + "ReputationOptions":{ + "shape":"ReputationOptions", + "documentation":"

    An object that defines whether or not Amazon SES collects reputation metrics for the emails that you send that use the configuration set.

    " + }, + "SendingOptions":{ + "shape":"SendingOptions", + "documentation":"

    An object that defines whether or not Amazon SES can send email that you send using the configuration set.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of objects that define the tags (keys and values) that you want to associate with the configuration set.

    " + }, + "SuppressionOptions":{"shape":"SuppressionOptions"} + }, + "documentation":"

    A request to create a configuration set.

    " + }, + "CreateConfigurationSetResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "CreateDedicatedIpPoolRequest":{ + "type":"structure", + "required":["PoolName"], + "members":{ + "PoolName":{ + "shape":"PoolName", + "documentation":"

    The name of the dedicated IP pool.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An object that defines the tags (keys and values) that you want to associate with the pool.

    " + } + }, + "documentation":"

    A request to create a new dedicated IP pool.

    " + }, + "CreateDedicatedIpPoolResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "CreateDeliverabilityTestReportRequest":{ + "type":"structure", + "required":[ + "FromEmailAddress", + "Content" + ], + "members":{ + "ReportName":{ + "shape":"ReportName", + "documentation":"

    A unique name that helps you to identify the predictive inbox placement test when you retrieve the results.

    " + }, + "FromEmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The email address that the predictive inbox placement test email was sent from.

    " + }, + "Content":{ + "shape":"EmailContent", + "documentation":"

    The HTML body of the message that you sent when you performed the predictive inbox placement test.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of objects that define the tags (keys and values) that you want to associate with the predictive inbox placement test.

    " + } + }, + "documentation":"

    A request to perform a predictive inbox placement test. Predictive inbox placement tests can help you predict how your messages will be handled by various email providers around the world. When you perform a predictive inbox placement test, you provide a sample message that contains the content that you plan to send to your customers. We send that message to special email addresses spread across several major email providers around the world. The test takes about 24 hours to complete. When the test is complete, you can use the GetDeliverabilityTestReport operation to view the results of the test.

    " + }, + "CreateDeliverabilityTestReportResponse":{ + "type":"structure", + "required":[ + "ReportId", + "DeliverabilityTestStatus" + ], + "members":{ + "ReportId":{ + "shape":"ReportId", + "documentation":"

    A unique string that identifies the predictive inbox placement test.

    " + }, + "DeliverabilityTestStatus":{ + "shape":"DeliverabilityTestStatus", + "documentation":"

    The status of the predictive inbox placement test. If the status is IN_PROGRESS, then the predictive inbox placement test is currently running. Predictive inbox placement tests are usually complete within 24 hours of creating the test. If the status is COMPLETE, then the test is finished, and you can use the GetDeliverabilityTestReport to view the results of the test.

    " + } + }, + "documentation":"

    Information about the predictive inbox placement test that you created.

    " + }, + "CreateEmailIdentityRequest":{ + "type":"structure", + "required":["EmailIdentity"], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The email address or domain that you want to verify.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of objects that define the tags (keys and values) that you want to associate with the email identity.

    " + }, + "DkimSigningAttributes":{ + "shape":"DkimSigningAttributes", + "documentation":"

    If your request includes this object, Amazon SES configures the identity to use Bring Your Own DKIM (BYODKIM) for DKIM authentication purposes, as opposed to the default method, Easy DKIM.

    You can only specify this object if the email identity is a domain, as opposed to an address.

    " + } + }, + "documentation":"

    A request to begin the verification process for an email identity (an email address or domain).

    " + }, + "CreateEmailIdentityResponse":{ + "type":"structure", + "members":{ + "IdentityType":{ + "shape":"IdentityType", + "documentation":"

    The email identity type.

    " + }, + "VerifiedForSendingStatus":{ + "shape":"Enabled", + "documentation":"

    Specifies whether or not the identity is verified. You can only send email from verified email addresses or domains. For more information about verifying identities, see the Amazon Pinpoint User Guide.

    " + }, + "DkimAttributes":{ + "shape":"DkimAttributes", + "documentation":"

    An object that contains information about the DKIM attributes for the identity.

    " + } + }, + "documentation":"

    If the email identity is a domain, this object contains information about the DKIM verification status for the domain.

    If the email identity is an email address, this object is empty.

    " + }, + "CustomRedirectDomain":{ + "type":"string", + "documentation":"

    The domain that you want to use for tracking open and click events.

    " + }, + "DailyVolume":{ + "type":"structure", + "members":{ + "StartDate":{ + "shape":"Timestamp", + "documentation":"

    The date that the DailyVolume metrics apply to, in Unix time.

    " + }, + "VolumeStatistics":{ + "shape":"VolumeStatistics", + "documentation":"

    An object that contains inbox placement metrics for a specific day in the analysis period.

    " + }, + "DomainIspPlacements":{ + "shape":"DomainIspPlacements", + "documentation":"

    An object that contains inbox placement metrics for a specified day in the analysis period, broken out by the recipient's email provider.

    " + } + }, + "documentation":"

    An object that contains information about the volume of email sent on each day of the analysis period.

    " + }, + "DailyVolumes":{ + "type":"list", + "member":{"shape":"DailyVolume"} + }, + "DedicatedIp":{ + "type":"structure", + "required":[ + "Ip", + "WarmupStatus", + "WarmupPercentage" + ], + "members":{ + "Ip":{ + "shape":"Ip", + "documentation":"

    An IPv4 address.

    " + }, + "WarmupStatus":{ + "shape":"WarmupStatus", + "documentation":"

    The warm-up status of a dedicated IP address. The status can have one of the following values:

    • IN_PROGRESS – The IP address isn't ready to use because the dedicated IP warm-up process is ongoing.

    • DONE – The dedicated IP warm-up process is complete, and the IP address is ready to use.

    " + }, + "WarmupPercentage":{ + "shape":"Percentage100Wrapper", + "documentation":"

    Indicates how complete the dedicated IP warm-up process is. When this value equals 1, the address has completed the warm-up process and is ready for use.

    " + }, + "PoolName":{ + "shape":"PoolName", + "documentation":"

    The name of the dedicated IP pool that the IP address is associated with.

    " + } + }, + "documentation":"

    Contains information about a dedicated IP address that is associated with your Amazon SES account.

    To learn more about requesting dedicated IP addresses, see Requesting and Relinquishing Dedicated IP Addresses in the Amazon SES Developer Guide.

    " + }, + "DedicatedIpList":{ + "type":"list", + "member":{"shape":"DedicatedIp"}, + "documentation":"

    A list of dedicated IP addresses that are associated with your AWS account.

    " + }, + "DefaultDimensionValue":{ + "type":"string", + "documentation":"

    The default value of the dimension that is published to Amazon CloudWatch if you don't provide the value of the dimension when you send an email. This value has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + }, + "DeleteConfigurationSetEventDestinationRequest":{ + "type":"structure", + "required":[ + "ConfigurationSetName", + "EventDestinationName" + ], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that contains the event destination that you want to delete.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "EventDestinationName":{ + "shape":"EventDestinationName", + "documentation":"

    The name of the event destination that you want to delete.

    ", + "location":"uri", + "locationName":"EventDestinationName" + } + }, + "documentation":"

    A request to delete an event destination from a configuration set.

    " + }, + "DeleteConfigurationSetEventDestinationResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "DeleteConfigurationSetRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to delete.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + } + }, + "documentation":"

    A request to delete a configuration set.

    " + }, + "DeleteConfigurationSetResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "DeleteDedicatedIpPoolRequest":{ + "type":"structure", + "required":["PoolName"], + "members":{ + "PoolName":{ + "shape":"PoolName", + "documentation":"

    The name of the dedicated IP pool that you want to delete.

    ", + "location":"uri", + "locationName":"PoolName" + } + }, + "documentation":"

    A request to delete a dedicated IP pool.

    " + }, + "DeleteDedicatedIpPoolResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "DeleteEmailIdentityRequest":{ + "type":"structure", + "required":["EmailIdentity"], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The identity (that is, the email address or domain) that you want to delete.

    ", + "location":"uri", + "locationName":"EmailIdentity" + } + }, + "documentation":"

    A request to delete an existing email identity. When you delete an identity, you lose the ability to send email from that identity. You can restore your ability to send email by completing the verification process for the identity again.

    " + }, + "DeleteEmailIdentityResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "DeleteSuppressedDestinationRequest":{ + "type":"structure", + "required":["EmailAddress"], + "members":{ + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The suppressed email destination to remove from the account suppression list.

    ", + "location":"uri", + "locationName":"EmailAddress" + } + }, + "documentation":"

    A request to remove an email address from the suppression list for your account.

    " + }, + "DeleteSuppressedDestinationResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "DeliverabilityDashboardAccountStatus":{ + "type":"string", + "documentation":"

    The current status of your Deliverability dashboard subscription. If this value is PENDING_EXPIRATION, your subscription is scheduled to expire at the end of the current calendar month.

    ", + "enum":[ + "ACTIVE", + "PENDING_EXPIRATION", + "DISABLED" + ] + }, + "DeliverabilityTestReport":{ + "type":"structure", + "members":{ + "ReportId":{ + "shape":"ReportId", + "documentation":"

    A unique string that identifies the predictive inbox placement test.

    " + }, + "ReportName":{ + "shape":"ReportName", + "documentation":"

    A name that helps you identify a predictive inbox placement test report.

    " + }, + "Subject":{ + "shape":"DeliverabilityTestSubject", + "documentation":"

    The subject line for an email that you submitted in a predictive inbox placement test.

    " + }, + "FromEmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The sender address that you specified for the predictive inbox placement test.

    " + }, + "CreateDate":{ + "shape":"Timestamp", + "documentation":"

    The date and time when the predictive inbox placement test was created, in Unix time format.

    " + }, + "DeliverabilityTestStatus":{ + "shape":"DeliverabilityTestStatus", + "documentation":"

    The status of the predictive inbox placement test. If the status is IN_PROGRESS, then the predictive inbox placement test is currently running. Predictive inbox placement tests are usually complete within 24 hours of creating the test. If the status is COMPLETE, then the test is finished, and you can use the GetDeliverabilityTestReport to view the results of the test.

    " + } + }, + "documentation":"

    An object that contains metadata related to a predictive inbox placement test.

    " + }, + "DeliverabilityTestReports":{ + "type":"list", + "member":{"shape":"DeliverabilityTestReport"} + }, + "DeliverabilityTestStatus":{ + "type":"string", + "documentation":"

    The status of a predictive inbox placement test. If the status is IN_PROGRESS, then the predictive inbox placement test is currently running. Predictive inbox placement tests are usually complete within 24 hours of creating the test. If the status is COMPLETE, then the test is finished, and you can use the GetDeliverabilityTestReport operation to view the results of the test.

    ", + "enum":[ + "IN_PROGRESS", + "COMPLETED" + ] + }, + "DeliverabilityTestSubject":{ + "type":"string", + "documentation":"

    The subject line for an email that you submitted in a predictive inbox placement test.

    " + }, + "DeliveryOptions":{ + "type":"structure", + "members":{ + "TlsPolicy":{ + "shape":"TlsPolicy", + "documentation":"

    Specifies whether messages that use the configuration set are required to use Transport Layer Security (TLS). If the value is Require, messages are only delivered if a TLS connection can be established. If the value is Optional, messages can be delivered in plain text if a TLS connection can't be established.

    " + }, + "SendingPoolName":{ + "shape":"PoolName", + "documentation":"

    The name of the dedicated IP pool that you want to associate with the configuration set.

    " + } + }, + "documentation":"

    Used to associate a configuration set with a dedicated IP pool.

    " + }, + "Destination":{ + "type":"structure", + "members":{ + "ToAddresses":{ + "shape":"EmailAddressList", + "documentation":"

    An array that contains the email addresses of the \"To\" recipients for the email.

    " + }, + "CcAddresses":{ + "shape":"EmailAddressList", + "documentation":"

    An array that contains the email addresses of the \"CC\" (carbon copy) recipients for the email.

    " + }, + "BccAddresses":{ + "shape":"EmailAddressList", + "documentation":"

    An array that contains the email addresses of the \"BCC\" (blind carbon copy) recipients for the email.

    " + } + }, + "documentation":"

    An object that describes the recipients for an email.

    " + }, + "DimensionName":{ + "type":"string", + "documentation":"

    The name of an Amazon CloudWatch dimension associated with an email sending metric. The name has to meet the following criteria:

    • It can only contain ASCII letters (a-z, A-Z), numbers (0-9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + }, + "DimensionValueSource":{ + "type":"string", + "documentation":"

    The location where the Amazon SES API v2 finds the value of a dimension to publish to Amazon CloudWatch. If you want to use the message tags that you specify using an X-SES-MESSAGE-TAGS header or a parameter to the SendEmail or SendRawEmail API, choose messageTag. If you want to use your own email headers, choose emailHeader. If you want to use link tags, choose linkTags.

    ", + "enum":[ + "MESSAGE_TAG", + "EMAIL_HEADER", + "LINK_TAG" + ] + }, + "DkimAttributes":{ + "type":"structure", + "members":{ + "SigningEnabled":{ + "shape":"Enabled", + "documentation":"

    If the value is true, then the messages that you send from the identity are signed using DKIM. If the value is false, then the messages that you send from the identity aren't DKIM-signed.

    " + }, + "Status":{ + "shape":"DkimStatus", + "documentation":"

    Describes whether or not Amazon SES has successfully located the DKIM records in the DNS records for the domain. The status can be one of the following:

    • PENDING – The verification process was initiated, but Amazon SES hasn't yet detected the DKIM records in the DNS configuration for the domain.

    • SUCCESS – The verification process completed successfully.

    • FAILED – The verification process failed. This typically occurs when Amazon SES fails to find the DKIM records in the DNS configuration of the domain.

    • TEMPORARY_FAILURE – A temporary issue is preventing Amazon SES from determining the DKIM authentication status of the domain.

    • NOT_STARTED – The DKIM verification process hasn't been initiated for the domain.

    " + }, + "Tokens":{ + "shape":"DnsTokenList", + "documentation":"

    If you used Easy DKIM to configure DKIM authentication for the domain, then this object contains a set of unique strings that you use to create a set of CNAME records that you add to the DNS configuration for your domain. When Amazon SES detects these records in the DNS configuration for your domain, the DKIM authentication process is complete.

    If you configured DKIM authentication for the domain by providing your own public-private key pair, then this object contains the selector for the public key.

    Regardless of the DKIM authentication method you use, Amazon SES searches for the appropriate records in the DNS configuration of the domain for up to 72 hours.

    " + }, + "SigningAttributesOrigin":{ + "shape":"DkimSigningAttributesOrigin", + "documentation":"

    A string that indicates how DKIM was configured for the identity. There are two possible values:

    • AWS_SES – Indicates that DKIM was configured for the identity by using Easy DKIM.

    • EXTERNAL – Indicates that DKIM was configured for the identity by using Bring Your Own DKIM (BYODKIM).

    " + } + }, + "documentation":"

    An object that contains information about the DKIM authentication status for an email identity.

    Amazon SES determines the authentication status by searching for specific records in the DNS configuration for the domain. If you used Easy DKIM to set up DKIM authentication, Amazon SES tries to find three unique CNAME records in the DNS configuration for your domain. If you provided a public key to perform DKIM authentication, Amazon SES tries to find a TXT record that uses the selector that you specified. The value of the TXT record must be a public key that's paired with the private key that you specified in the process of creating the identity

    " + }, + "DkimSigningAttributes":{ + "type":"structure", + "required":[ + "DomainSigningSelector", + "DomainSigningPrivateKey" + ], + "members":{ + "DomainSigningSelector":{ + "shape":"Selector", + "documentation":"

    A string that's used to identify a public key in the DNS configuration for a domain.

    " + }, + "DomainSigningPrivateKey":{ + "shape":"PrivateKey", + "documentation":"

    A private key that's used to generate a DKIM signature.

    The private key must use 1024-bit RSA encryption, and must be encoded using base64 encoding.

    " + } + }, + "documentation":"

    An object that contains information about the tokens used for setting up Bring Your Own DKIM (BYODKIM).

    " + }, + "DkimSigningAttributesOrigin":{ + "type":"string", + "enum":[ + "AWS_SES", + "EXTERNAL" + ] + }, + "DkimStatus":{ + "type":"string", + "documentation":"

    The DKIM authentication status of the identity. The status can be one of the following:

    • PENDING – The verification process was initiated, but Amazon SES hasn't yet detected the DKIM records in the DNS configuration for the domain.

    • SUCCESS – The verification process completed successfully.

    • FAILED – The verification process failed. This typically occurs when Amazon SES fails to find the DKIM records in the DNS configuration of the domain.

    • TEMPORARY_FAILURE – A temporary issue is preventing Amazon SES from determining the DKIM authentication status of the domain.

    • NOT_STARTED – The DKIM verification process hasn't been initiated for the domain.

    ", + "enum":[ + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE", + "NOT_STARTED" + ] + }, + "DnsToken":{"type":"string"}, + "DnsTokenList":{ + "type":"list", + "member":{"shape":"DnsToken"} + }, + "Domain":{"type":"string"}, + "DomainDeliverabilityCampaign":{ + "type":"structure", + "members":{ + "CampaignId":{ + "shape":"CampaignId", + "documentation":"

    The unique identifier for the campaign. The Deliverability dashboard automatically generates and assigns this identifier to a campaign.

    " + }, + "ImageUrl":{ + "shape":"ImageUrl", + "documentation":"

    The URL of an image that contains a snapshot of the email message that was sent.

    " + }, + "Subject":{ + "shape":"Subject", + "documentation":"

    The subject line, or title, of the email message.

    " + }, + "FromAddress":{ + "shape":"Identity", + "documentation":"

    The verified email address that the email message was sent from.

    " + }, + "SendingIps":{ + "shape":"IpList", + "documentation":"

    The IP addresses that were used to send the email message.

    " + }, + "FirstSeenDateTime":{ + "shape":"Timestamp", + "documentation":"

    The first time, in Unix time format, when the email message was delivered to any recipient's inbox. This value can help you determine how long it took for a campaign to deliver an email message.

    " + }, + "LastSeenDateTime":{ + "shape":"Timestamp", + "documentation":"

    The last time, in Unix time format, when the email message was delivered to any recipient's inbox. This value can help you determine how long it took for a campaign to deliver an email message.

    " + }, + "InboxCount":{ + "shape":"Volume", + "documentation":"

    The number of email messages that were delivered to recipients’ inboxes.

    " + }, + "SpamCount":{ + "shape":"Volume", + "documentation":"

    The number of email messages that were delivered to recipients' spam or junk mail folders.

    " + }, + "ReadRate":{ + "shape":"Percentage", + "documentation":"

    The percentage of email messages that were opened by recipients. Due to technical limitations, this value only includes recipients who opened the message by using an email client that supports images.

    " + }, + "DeleteRate":{ + "shape":"Percentage", + "documentation":"

    The percentage of email messages that were deleted by recipients, without being opened first. Due to technical limitations, this value only includes recipients who opened the message by using an email client that supports images.

    " + }, + "ReadDeleteRate":{ + "shape":"Percentage", + "documentation":"

    The percentage of email messages that were opened and then deleted by recipients. Due to technical limitations, this value only includes recipients who opened the message by using an email client that supports images.

    " + }, + "ProjectedVolume":{ + "shape":"Volume", + "documentation":"

    The projected number of recipients that the email message was sent to.

    " + }, + "Esps":{ + "shape":"Esps", + "documentation":"

    The major email providers who handled the email message.

    " + } + }, + "documentation":"

    An object that contains the deliverability data for a specific campaign. This data is available for a campaign only if the campaign sent email by using a domain that the Deliverability dashboard is enabled for (PutDeliverabilityDashboardOption operation).

    " + }, + "DomainDeliverabilityCampaignList":{ + "type":"list", + "member":{"shape":"DomainDeliverabilityCampaign"}, + "documentation":"

    " + }, + "DomainDeliverabilityTrackingOption":{ + "type":"structure", + "members":{ + "Domain":{ + "shape":"Domain", + "documentation":"

    A verified domain that’s associated with your AWS account and currently has an active Deliverability dashboard subscription.

    " + }, + "SubscriptionStartDate":{ + "shape":"Timestamp", + "documentation":"

    The date, in Unix time format, when you enabled the Deliverability dashboard for the domain.

    " + }, + "InboxPlacementTrackingOption":{ + "shape":"InboxPlacementTrackingOption", + "documentation":"

    An object that contains information about the inbox placement data settings for the domain.

    " + } + }, + "documentation":"

    An object that contains information about the Deliverability dashboard subscription for a verified domain that you use to send email and currently has an active Deliverability dashboard subscription. If a Deliverability dashboard subscription is active for a domain, you gain access to reputation, inbox placement, and other metrics for the domain.

    " + }, + "DomainDeliverabilityTrackingOptions":{ + "type":"list", + "member":{"shape":"DomainDeliverabilityTrackingOption"}, + "documentation":"

    An object that contains information about the Deliverability dashboard subscription for a verified domain that you use to send email and currently has an active Deliverability dashboard subscription. If a Deliverability dashboard subscription is active for a domain, you gain access to reputation, inbox placement, and other metrics for the domain.

    " + }, + "DomainIspPlacement":{ + "type":"structure", + "members":{ + "IspName":{ + "shape":"IspName", + "documentation":"

    The name of the email provider that the inbox placement data applies to.

    " + }, + "InboxRawCount":{ + "shape":"Volume", + "documentation":"

    The total number of messages that were sent from the selected domain to the specified email provider that arrived in recipients' inboxes.

    " + }, + "SpamRawCount":{ + "shape":"Volume", + "documentation":"

    The total number of messages that were sent from the selected domain to the specified email provider that arrived in recipients' spam or junk mail folders.

    " + }, + "InboxPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of messages that were sent from the selected domain to the specified email provider that arrived in recipients' inboxes.

    " + }, + "SpamPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of messages that were sent from the selected domain to the specified email provider that arrived in recipients' spam or junk mail folders.

    " + } + }, + "documentation":"

    An object that contains inbox placement data for email sent from one of your email domains to a specific email provider.

    " + }, + "DomainIspPlacements":{ + "type":"list", + "member":{"shape":"DomainIspPlacement"} + }, + "EmailAddress":{"type":"string"}, + "EmailAddressList":{ + "type":"list", + "member":{"shape":"EmailAddress"} + }, + "EmailContent":{ + "type":"structure", + "members":{ + "Simple":{ + "shape":"Message", + "documentation":"

    The simple email message. The message consists of a subject and a message body.

    " + }, + "Raw":{ + "shape":"RawMessage", + "documentation":"

    The raw email message. The message has to meet the following criteria:

    • The message has to contain a header and a body, separated by one blank line.

    • All of the required header fields must be present in the message.

    • Each part of a multipart MIME message must be formatted properly.

    • If you include attachments, they must be in a file format that the Amazon SES API v2 supports.

    • The entire message must be Base64 encoded.

    • If any of the MIME parts in your message contain content that is outside of the 7-bit ASCII character range, you should encode that content to ensure that recipients' email clients render the message properly.

    • The length of any single line of text in the message can't exceed 1,000 characters. This restriction is defined in RFC 5321.

    " + }, + "Template":{ + "shape":"Template", + "documentation":"

    The template to use for the email message.

    " + } + }, + "documentation":"

    An object that defines the entire content of the email, including the message headers and the body content. You can create a simple email message, in which you specify the subject and the text and HTML versions of the message body. You can also create raw messages, in which you specify a complete MIME-formatted message. Raw messages can include attachments and custom headers.

    " + }, + "Enabled":{"type":"boolean"}, + "Esp":{"type":"string"}, + "Esps":{ + "type":"list", + "member":{"shape":"Esp"} + }, + "EventDestination":{ + "type":"structure", + "required":[ + "Name", + "MatchingEventTypes" + ], + "members":{ + "Name":{ + "shape":"EventDestinationName", + "documentation":"

    A name that identifies the event destination.

    " + }, + "Enabled":{ + "shape":"Enabled", + "documentation":"

    If true, the event destination is enabled. When the event destination is enabled, the specified event types are sent to the destinations in this EventDestinationDefinition.

    If false, the event destination is disabled. When the event destination is disabled, events aren't sent to the specified destinations.

    " + }, + "MatchingEventTypes":{ + "shape":"EventTypes", + "documentation":"

    The types of events that Amazon SES sends to the specified event destinations.

    " + }, + "KinesisFirehoseDestination":{ + "shape":"KinesisFirehoseDestination", + "documentation":"

    An object that defines an Amazon Kinesis Data Firehose destination for email events. You can use Amazon Kinesis Data Firehose to stream data to other services, such as Amazon S3 and Amazon Redshift.

    " + }, + "CloudWatchDestination":{ + "shape":"CloudWatchDestination", + "documentation":"

    An object that defines an Amazon CloudWatch destination for email events. You can use Amazon CloudWatch to monitor and gain insights on your email sending metrics.

    " + }, + "SnsDestination":{ + "shape":"SnsDestination", + "documentation":"

    An object that defines an Amazon SNS destination for email events. You can use Amazon SNS to send notification when certain email events occur.

    " + }, + "PinpointDestination":{ + "shape":"PinpointDestination", + "documentation":"

    An object that defines an Amazon Pinpoint project destination for email events. You can send email event data to a Amazon Pinpoint project to view metrics using the Transactional Messaging dashboards that are built in to Amazon Pinpoint. For more information, see Transactional Messaging Charts in the Amazon Pinpoint User Guide.

    " + } + }, + "documentation":"

    In the Amazon SES API v2, events include message sends, deliveries, opens, clicks, bounces, and complaints. Event destinations are places that you can send information about these events to. For example, you can send event data to Amazon SNS to receive notifications when you receive bounces or complaints, or you can use Amazon Kinesis Data Firehose to stream data to Amazon S3 for long-term storage.

    " + }, + "EventDestinationDefinition":{ + "type":"structure", + "members":{ + "Enabled":{ + "shape":"Enabled", + "documentation":"

    If true, the event destination is enabled. When the event destination is enabled, the specified event types are sent to the destinations in this EventDestinationDefinition.

    If false, the event destination is disabled. When the event destination is disabled, events aren't sent to the specified destinations.

    " + }, + "MatchingEventTypes":{ + "shape":"EventTypes", + "documentation":"

    An array that specifies which events the Amazon SES API v2 should send to the destinations in this EventDestinationDefinition.

    " + }, + "KinesisFirehoseDestination":{ + "shape":"KinesisFirehoseDestination", + "documentation":"

    An object that defines an Amazon Kinesis Data Firehose destination for email events. You can use Amazon Kinesis Data Firehose to stream data to other services, such as Amazon S3 and Amazon Redshift.

    " + }, + "CloudWatchDestination":{ + "shape":"CloudWatchDestination", + "documentation":"

    An object that defines an Amazon CloudWatch destination for email events. You can use Amazon CloudWatch to monitor and gain insights on your email sending metrics.

    " + }, + "SnsDestination":{ + "shape":"SnsDestination", + "documentation":"

    An object that defines an Amazon SNS destination for email events. You can use Amazon SNS to send notification when certain email events occur.

    " + }, + "PinpointDestination":{ + "shape":"PinpointDestination", + "documentation":"

    An object that defines an Amazon Pinpoint project destination for email events. You can send email event data to a Amazon Pinpoint project to view metrics using the Transactional Messaging dashboards that are built in to Amazon Pinpoint. For more information, see Transactional Messaging Charts in the Amazon Pinpoint User Guide.

    " + } + }, + "documentation":"

    An object that defines the event destination. Specifically, it defines which services receive events from emails sent using the configuration set that the event destination is associated with. Also defines the types of events that are sent to the event destination.

    " + }, + "EventDestinationName":{ + "type":"string", + "documentation":"

    The name of an event destination.

    Events include message sends, deliveries, opens, clicks, bounces, and complaints. Event destinations are places that you can send information about these events to. For example, you can send event data to Amazon SNS to receive notifications when you receive bounces or complaints, or you can use Amazon Kinesis Data Firehose to stream data to Amazon S3 for long-term storage.

    " + }, + "EventDestinations":{ + "type":"list", + "member":{"shape":"EventDestination"} + }, + "EventType":{ + "type":"string", + "documentation":"

    An email sending event type. For example, email sends, opens, and bounces are all email events.

    ", + "enum":[ + "SEND", + "REJECT", + "BOUNCE", + "COMPLAINT", + "DELIVERY", + "OPEN", + "CLICK", + "RENDERING_FAILURE" + ] + }, + "EventTypes":{ + "type":"list", + "member":{"shape":"EventType"} + }, + "FeedbackId":{"type":"string"}, + "GeneralEnforcementStatus":{"type":"string"}, + "GetAccountRequest":{ + "type":"structure", + "members":{ + }, + "documentation":"

    A request to obtain information about the email-sending capabilities of your Amazon SES account.

    " + }, + "GetAccountResponse":{ + "type":"structure", + "members":{ + "DedicatedIpAutoWarmupEnabled":{ + "shape":"Enabled", + "documentation":"

    Indicates whether or not the automatic warm-up feature is enabled for dedicated IP addresses that are associated with your account.

    " + }, + "EnforcementStatus":{ + "shape":"GeneralEnforcementStatus", + "documentation":"

    The reputation status of your Amazon SES account. The status can be one of the following:

    • HEALTHY – There are no reputation-related issues that currently impact your account.

    • PROBATION – We've identified potential issues with your Amazon SES account. We're placing your account under review while you work on correcting these issues.

    • SHUTDOWN – Your account's ability to send email is currently paused because of an issue with the email sent from your account. When you correct the issue, you can contact us and request that your account's ability to send email is resumed.

    " + }, + "ProductionAccessEnabled":{ + "shape":"Enabled", + "documentation":"

    Indicates whether or not your account has production access in the current AWS Region.

    If the value is false, then your account is in the sandbox. When your account is in the sandbox, you can only send email to verified identities. Additionally, the maximum number of emails you can send in a 24-hour period (your sending quota) is 200, and the maximum number of emails you can send per second (your maximum sending rate) is 1.

    If the value is true, then your account has production access. When your account has production access, you can send email to any address. The sending quota and maximum sending rate for your account vary based on your specific use case.

    " + }, + "SendQuota":{ + "shape":"SendQuota", + "documentation":"

    An object that contains information about the per-day and per-second sending limits for your Amazon SES account in the current AWS Region.

    " + }, + "SendingEnabled":{ + "shape":"Enabled", + "documentation":"

    Indicates whether or not email sending is enabled for your Amazon SES account in the current AWS Region.

    " + }, + "SuppressionAttributes":{ + "shape":"SuppressionAttributes", + "documentation":"

    An object that contains information about the email address suppression preferences for your account in the current AWS Region.

    " + } + }, + "documentation":"

    A list of details about the email-sending capabilities of your Amazon SES account in the current AWS Region.

    " + }, + "GetBlacklistReportsRequest":{ + "type":"structure", + "required":["BlacklistItemNames"], + "members":{ + "BlacklistItemNames":{ + "shape":"BlacklistItemNames", + "documentation":"

    A list of IP addresses that you want to retrieve blacklist information about. You can only specify the dedicated IP addresses that you use to send email using Amazon SES or Amazon Pinpoint.

    ", + "location":"querystring", + "locationName":"BlacklistItemNames" + } + }, + "documentation":"

    A request to retrieve a list of the blacklists that your dedicated IP addresses appear on.

    " + }, + "GetBlacklistReportsResponse":{ + "type":"structure", + "required":["BlacklistReport"], + "members":{ + "BlacklistReport":{ + "shape":"BlacklistReport", + "documentation":"

    An object that contains information about a blacklist that one of your dedicated IP addresses appears on.

    " + } + }, + "documentation":"

    An object that contains information about blacklist events.

    " + }, + "GetConfigurationSetEventDestinationsRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that contains the event destination.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + } + }, + "documentation":"

    A request to obtain information about the event destinations for a configuration set.

    " + }, + "GetConfigurationSetEventDestinationsResponse":{ + "type":"structure", + "members":{ + "EventDestinations":{ + "shape":"EventDestinations", + "documentation":"

    An array that includes all of the events destinations that have been configured for the configuration set.

    " + } + }, + "documentation":"

    Information about an event destination for a configuration set.

    " + }, + "GetConfigurationSetRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to obtain more information about.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + } + }, + "documentation":"

    A request to obtain information about a configuration set.

    " + }, + "GetConfigurationSetResponse":{ + "type":"structure", + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set.

    " + }, + "TrackingOptions":{ + "shape":"TrackingOptions", + "documentation":"

    An object that defines the open and click tracking options for emails that you send using the configuration set.

    " + }, + "DeliveryOptions":{ + "shape":"DeliveryOptions", + "documentation":"

    An object that defines the dedicated IP pool that is used to send emails that you send using the configuration set.

    " + }, + "ReputationOptions":{ + "shape":"ReputationOptions", + "documentation":"

    An object that defines whether or not Amazon SES collects reputation metrics for the emails that you send that use the configuration set.

    " + }, + "SendingOptions":{ + "shape":"SendingOptions", + "documentation":"

    An object that defines whether or not Amazon SES can send email that you send using the configuration set.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of objects that define the tags (keys and values) that are associated with the configuration set.

    " + }, + "SuppressionOptions":{ + "shape":"SuppressionOptions", + "documentation":"

    An object that contains information about the suppression list preferences for your account.

    " + } + }, + "documentation":"

    Information about a configuration set.

    " + }, + "GetDedicatedIpRequest":{ + "type":"structure", + "required":["Ip"], + "members":{ + "Ip":{ + "shape":"Ip", + "documentation":"

    The IP address that you want to obtain more information about. The value you specify has to be a dedicated IP address that's assocaited with your AWS account.

    ", + "location":"uri", + "locationName":"IP" + } + }, + "documentation":"

    A request to obtain more information about a dedicated IP address.

    " + }, + "GetDedicatedIpResponse":{ + "type":"structure", + "members":{ + "DedicatedIp":{ + "shape":"DedicatedIp", + "documentation":"

    An object that contains information about a dedicated IP address.

    " + } + }, + "documentation":"

    Information about a dedicated IP address.

    " + }, + "GetDedicatedIpsRequest":{ + "type":"structure", + "members":{ + "PoolName":{ + "shape":"PoolName", + "documentation":"

    The name of the IP pool that the dedicated IP address is associated with.

    ", + "location":"querystring", + "locationName":"PoolName" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token returned from a previous call to GetDedicatedIps to indicate the position of the dedicated IP pool in the list of IP pools.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The number of results to show in a single call to GetDedicatedIpsRequest. If the number of results is larger than the number you specified in this parameter, then the response includes a NextToken element, which you can use to obtain additional results.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    A request to obtain more information about dedicated IP pools.

    " + }, + "GetDedicatedIpsResponse":{ + "type":"structure", + "members":{ + "DedicatedIps":{ + "shape":"DedicatedIpList", + "documentation":"

    A list of dedicated IP addresses that are associated with your AWS account.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that indicates that there are additional dedicated IP addresses to list. To view additional addresses, issue another request to GetDedicatedIps, passing this token in the NextToken parameter.

    " + } + }, + "documentation":"

    Information about the dedicated IP addresses that are associated with your AWS account.

    " + }, + "GetDeliverabilityDashboardOptionsRequest":{ + "type":"structure", + "members":{ + }, + "documentation":"

    Retrieve information about the status of the Deliverability dashboard for your AWS account. When the Deliverability dashboard is enabled, you gain access to reputation, deliverability, and other metrics for your domains. You also gain the ability to perform predictive inbox placement tests.

    When you use the Deliverability dashboard, you pay a monthly subscription charge, in addition to any other fees that you accrue by using Amazon SES and other AWS services. For more information about the features and cost of a Deliverability dashboard subscription, see Amazon Pinpoint Pricing.

    " + }, + "GetDeliverabilityDashboardOptionsResponse":{ + "type":"structure", + "required":["DashboardEnabled"], + "members":{ + "DashboardEnabled":{ + "shape":"Enabled", + "documentation":"

    Specifies whether the Deliverability dashboard is enabled. If this value is true, the dashboard is enabled.

    " + }, + "SubscriptionExpiryDate":{ + "shape":"Timestamp", + "documentation":"

    The date, in Unix time format, when your current subscription to the Deliverability dashboard is scheduled to expire, if your subscription is scheduled to expire at the end of the current calendar month. This value is null if you have an active subscription that isn’t due to expire at the end of the month.

    " + }, + "AccountStatus":{ + "shape":"DeliverabilityDashboardAccountStatus", + "documentation":"

    The current status of your Deliverability dashboard subscription. If this value is PENDING_EXPIRATION, your subscription is scheduled to expire at the end of the current calendar month.

    " + }, + "ActiveSubscribedDomains":{ + "shape":"DomainDeliverabilityTrackingOptions", + "documentation":"

    An array of objects, one for each verified domain that you use to send email and currently has an active Deliverability dashboard subscription that isn’t scheduled to expire at the end of the current calendar month.

    " + }, + "PendingExpirationSubscribedDomains":{ + "shape":"DomainDeliverabilityTrackingOptions", + "documentation":"

    An array of objects, one for each verified domain that you use to send email and currently has an active Deliverability dashboard subscription that's scheduled to expire at the end of the current calendar month.

    " + } + }, + "documentation":"

    An object that shows the status of the Deliverability dashboard.

    " + }, + "GetDeliverabilityTestReportRequest":{ + "type":"structure", + "required":["ReportId"], + "members":{ + "ReportId":{ + "shape":"ReportId", + "documentation":"

    A unique string that identifies the predictive inbox placement test.

    ", + "location":"uri", + "locationName":"ReportId" + } + }, + "documentation":"

    A request to retrieve the results of a predictive inbox placement test.

    " + }, + "GetDeliverabilityTestReportResponse":{ + "type":"structure", + "required":[ + "DeliverabilityTestReport", + "OverallPlacement", + "IspPlacements" + ], + "members":{ + "DeliverabilityTestReport":{ + "shape":"DeliverabilityTestReport", + "documentation":"

    An object that contains the results of the predictive inbox placement test.

    " + }, + "OverallPlacement":{ + "shape":"PlacementStatistics", + "documentation":"

    An object that specifies how many test messages that were sent during the predictive inbox placement test were delivered to recipients' inboxes, how many were sent to recipients' spam folders, and how many weren't delivered.

    " + }, + "IspPlacements":{ + "shape":"IspPlacements", + "documentation":"

    An object that describes how the test email was handled by several email providers, including Gmail, Hotmail, Yahoo, AOL, and others.

    " + }, + "Message":{ + "shape":"MessageContent", + "documentation":"

    An object that contains the message that you sent when you performed this predictive inbox placement test.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of objects that define the tags (keys and values) that are associated with the predictive inbox placement test.

    " + } + }, + "documentation":"

    The results of the predictive inbox placement test.

    " + }, + "GetDomainDeliverabilityCampaignRequest":{ + "type":"structure", + "required":["CampaignId"], + "members":{ + "CampaignId":{ + "shape":"CampaignId", + "documentation":"

    The unique identifier for the campaign. The Deliverability dashboard automatically generates and assigns this identifier to a campaign.

    ", + "location":"uri", + "locationName":"CampaignId" + } + }, + "documentation":"

    Retrieve all the deliverability data for a specific campaign. This data is available for a campaign only if the campaign sent email by using a domain that the Deliverability dashboard is enabled for (PutDeliverabilityDashboardOption operation).

    " + }, + "GetDomainDeliverabilityCampaignResponse":{ + "type":"structure", + "required":["DomainDeliverabilityCampaign"], + "members":{ + "DomainDeliverabilityCampaign":{ + "shape":"DomainDeliverabilityCampaign", + "documentation":"

    An object that contains the deliverability data for the campaign.

    " + } + }, + "documentation":"

    An object that contains all the deliverability data for a specific campaign. This data is available for a campaign only if the campaign sent email by using a domain that the Deliverability dashboard is enabled for.

    " + }, + "GetDomainStatisticsReportRequest":{ + "type":"structure", + "required":[ + "Domain", + "StartDate", + "EndDate" + ], + "members":{ + "Domain":{ + "shape":"Identity", + "documentation":"

    The domain that you want to obtain deliverability metrics for.

    ", + "location":"uri", + "locationName":"Domain" + }, + "StartDate":{ + "shape":"Timestamp", + "documentation":"

    The first day (in Unix time) that you want to obtain domain deliverability metrics for.

    ", + "location":"querystring", + "locationName":"StartDate" + }, + "EndDate":{ + "shape":"Timestamp", + "documentation":"

    The last day (in Unix time) that you want to obtain domain deliverability metrics for. The EndDate that you specify has to be less than or equal to 30 days after the StartDate.

    ", + "location":"querystring", + "locationName":"EndDate" + } + }, + "documentation":"

    A request to obtain deliverability metrics for a domain.

    " + }, + "GetDomainStatisticsReportResponse":{ + "type":"structure", + "required":[ + "OverallVolume", + "DailyVolumes" + ], + "members":{ + "OverallVolume":{ + "shape":"OverallVolume", + "documentation":"

    An object that contains deliverability metrics for the domain that you specified. The data in this object is a summary of all of the data that was collected from the StartDate to the EndDate.

    " + }, + "DailyVolumes":{ + "shape":"DailyVolumes", + "documentation":"

    An object that contains deliverability metrics for the domain that you specified. This object contains data for each day, starting on the StartDate and ending on the EndDate.

    " + } + }, + "documentation":"

    An object that includes statistics that are related to the domain that you specified.

    " + }, + "GetEmailIdentityRequest":{ + "type":"structure", + "required":["EmailIdentity"], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The email identity that you want to retrieve details for.

    ", + "location":"uri", + "locationName":"EmailIdentity" + } + }, + "documentation":"

    A request to return details about an email identity.

    " + }, + "GetEmailIdentityResponse":{ + "type":"structure", + "members":{ + "IdentityType":{ + "shape":"IdentityType", + "documentation":"

    The email identity type.

    " + }, + "FeedbackForwardingStatus":{ + "shape":"Enabled", + "documentation":"

    The feedback forwarding configuration for the identity.

    If the value is true, you receive email notifications when bounce or complaint events occur. These notifications are sent to the address that you specified in the Return-Path header of the original email.

    You're required to have a method of tracking bounces and complaints. If you haven't set up another mechanism for receiving bounce or complaint notifications (for example, by setting up an event destination), you receive an email notification when these events occur (even if this setting is disabled).

    " + }, + "VerifiedForSendingStatus":{ + "shape":"Enabled", + "documentation":"

    Specifies whether or not the identity is verified. You can only send email from verified email addresses or domains. For more information about verifying identities, see the Amazon Pinpoint User Guide.

    " + }, + "DkimAttributes":{ + "shape":"DkimAttributes", + "documentation":"

    An object that contains information about the DKIM attributes for the identity.

    " + }, + "MailFromAttributes":{ + "shape":"MailFromAttributes", + "documentation":"

    An object that contains information about the Mail-From attributes for the email identity.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of objects that define the tags (keys and values) that are associated with the email identity.

    " + } + }, + "documentation":"

    Details about an email identity.

    " + }, + "GetSuppressedDestinationRequest":{ + "type":"structure", + "required":["EmailAddress"], + "members":{ + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The email address that's on the account suppression list.

    ", + "location":"uri", + "locationName":"EmailAddress" + } + }, + "documentation":"

    A request to retrieve information about an email address that's on the suppression list for your account.

    " + }, + "GetSuppressedDestinationResponse":{ + "type":"structure", + "required":["SuppressedDestination"], + "members":{ + "SuppressedDestination":{ + "shape":"SuppressedDestination", + "documentation":"

    An object containing information about the suppressed email address.

    " + } + }, + "documentation":"

    Information about the suppressed email address.

    " + }, + "Identity":{"type":"string"}, + "IdentityInfo":{ + "type":"structure", + "members":{ + "IdentityType":{ + "shape":"IdentityType", + "documentation":"

    The email identity type. The identity type can be one of the following:

    • EMAIL_ADDRESS – The identity is an email address.

    • DOMAIN – The identity is a domain.

    • MANAGED_DOMAIN – The identity is a domain that is managed by AWS.

    " + }, + "IdentityName":{ + "shape":"Identity", + "documentation":"

    The address or domain of the identity.

    " + }, + "SendingEnabled":{ + "shape":"Enabled", + "documentation":"

    Indicates whether or not you can send email from the identity.

    An identity is an email address or domain that you send email from. Before you can send email from an identity, you have to demostrate that you own the identity, and that you authorize Amazon SES to send email from that identity.

    " + } + }, + "documentation":"

    Information about an email identity.

    " + }, + "IdentityInfoList":{ + "type":"list", + "member":{"shape":"IdentityInfo"} + }, + "IdentityType":{ + "type":"string", + "documentation":"

    The email identity type. The identity type can be one of the following:

    • EMAIL_ADDRESS – The identity is an email address.

    • DOMAIN – The identity is a domain.

    ", + "enum":[ + "EMAIL_ADDRESS", + "DOMAIN", + "MANAGED_DOMAIN" + ] + }, + "ImageUrl":{"type":"string"}, + "InboxPlacementTrackingOption":{ + "type":"structure", + "members":{ + "Global":{ + "shape":"Enabled", + "documentation":"

    Specifies whether inbox placement data is being tracked for the domain.

    " + }, + "TrackedIsps":{ + "shape":"IspNameList", + "documentation":"

    An array of strings, one for each major email provider that the inbox placement data applies to.

    " + } + }, + "documentation":"

    An object that contains information about the inbox placement data settings for a verified domain that’s associated with your AWS account. This data is available only if you enabled the Deliverability dashboard for the domain.

    " + }, + "InvalidNextTokenException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The specified request includes an invalid or expired token.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "Ip":{ + "type":"string", + "documentation":"

    An IPv4 address.

    " + }, + "IpList":{ + "type":"list", + "member":{"shape":"Ip"} + }, + "IspName":{ + "type":"string", + "documentation":"

    The name of an email provider.

    " + }, + "IspNameList":{ + "type":"list", + "member":{"shape":"IspName"} + }, + "IspPlacement":{ + "type":"structure", + "members":{ + "IspName":{ + "shape":"IspName", + "documentation":"

    The name of the email provider that the inbox placement data applies to.

    " + }, + "PlacementStatistics":{ + "shape":"PlacementStatistics", + "documentation":"

    An object that contains inbox placement metrics for a specific email provider.

    " + } + }, + "documentation":"

    An object that describes how email sent during the predictive inbox placement test was handled by a certain email provider.

    " + }, + "IspPlacements":{ + "type":"list", + "member":{"shape":"IspPlacement"} + }, + "KinesisFirehoseDestination":{ + "type":"structure", + "required":[ + "IamRoleArn", + "DeliveryStreamArn" + ], + "members":{ + "IamRoleArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the IAM role that the Amazon SES API v2 uses to send email events to the Amazon Kinesis Data Firehose stream.

    " + }, + "DeliveryStreamArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the Amazon Kinesis Data Firehose stream that the Amazon SES API v2 sends email events to.

    " + } + }, + "documentation":"

    An object that defines an Amazon Kinesis Data Firehose destination for email events. You can use Amazon Kinesis Data Firehose to stream data to other services, such as Amazon S3 and Amazon Redshift.

    " + }, + "LastFreshStart":{ + "type":"timestamp", + "documentation":"

    The date and time (in Unix time) when the reputation metrics were last given a fresh start. When your account is given a fresh start, your reputation metrics are calculated starting from the date of the fresh start.

    " + }, + "LimitExceededException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    There are too many instances of the specified resource type.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ListConfigurationSetsRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token returned from a previous call to ListConfigurationSets to indicate the position in the list of configuration sets.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The number of results to show in a single call to ListConfigurationSets. If the number of results is larger than the number you specified in this parameter, then the response includes a NextToken element, which you can use to obtain additional results.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    A request to obtain a list of configuration sets for your Amazon SES account in the current AWS Region.

    " + }, + "ListConfigurationSetsResponse":{ + "type":"structure", + "members":{ + "ConfigurationSets":{ + "shape":"ConfigurationSetNameList", + "documentation":"

    An array that contains all of the configuration sets in your Amazon SES account in the current AWS Region.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that indicates that there are additional configuration sets to list. To view additional configuration sets, issue another request to ListConfigurationSets, and pass this token in the NextToken parameter.

    " + } + }, + "documentation":"

    A list of configuration sets in your Amazon SES account in the current AWS Region.

    " + }, + "ListDedicatedIpPoolsRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token returned from a previous call to ListDedicatedIpPools to indicate the position in the list of dedicated IP pools.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The number of results to show in a single call to ListDedicatedIpPools. If the number of results is larger than the number you specified in this parameter, then the response includes a NextToken element, which you can use to obtain additional results.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    A request to obtain a list of dedicated IP pools.

    " + }, + "ListDedicatedIpPoolsResponse":{ + "type":"structure", + "members":{ + "DedicatedIpPools":{ + "shape":"ListOfDedicatedIpPools", + "documentation":"

    A list of all of the dedicated IP pools that are associated with your AWS account in the current Region.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that indicates that there are additional IP pools to list. To view additional IP pools, issue another request to ListDedicatedIpPools, passing this token in the NextToken parameter.

    " + } + }, + "documentation":"

    A list of dedicated IP pools.

    " + }, + "ListDeliverabilityTestReportsRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token returned from a previous call to ListDeliverabilityTestReports to indicate the position in the list of predictive inbox placement tests.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The number of results to show in a single call to ListDeliverabilityTestReports. If the number of results is larger than the number you specified in this parameter, then the response includes a NextToken element, which you can use to obtain additional results.

    The value you specify has to be at least 0, and can be no more than 1000.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    A request to list all of the predictive inbox placement tests that you've performed.

    " + }, + "ListDeliverabilityTestReportsResponse":{ + "type":"structure", + "required":["DeliverabilityTestReports"], + "members":{ + "DeliverabilityTestReports":{ + "shape":"DeliverabilityTestReports", + "documentation":"

    An object that contains a lists of predictive inbox placement tests that you've performed.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that indicates that there are additional predictive inbox placement tests to list. To view additional predictive inbox placement tests, issue another request to ListDeliverabilityTestReports, and pass this token in the NextToken parameter.

    " + } + }, + "documentation":"

    A list of the predictive inbox placement test reports that are available for your account, regardless of whether or not those tests are complete.

    " + }, + "ListDomainDeliverabilityCampaignsRequest":{ + "type":"structure", + "required":[ + "StartDate", + "EndDate", + "SubscribedDomain" + ], + "members":{ + "StartDate":{ + "shape":"Timestamp", + "documentation":"

    The first day, in Unix time format, that you want to obtain deliverability data for.

    ", + "location":"querystring", + "locationName":"StartDate" + }, + "EndDate":{ + "shape":"Timestamp", + "documentation":"

    The last day, in Unix time format, that you want to obtain deliverability data for. This value has to be less than or equal to 30 days after the value of the StartDate parameter.

    ", + "location":"querystring", + "locationName":"EndDate" + }, + "SubscribedDomain":{ + "shape":"Domain", + "documentation":"

    The domain to obtain deliverability data for.

    ", + "location":"uri", + "locationName":"SubscribedDomain" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that’s returned from a previous call to the ListDomainDeliverabilityCampaigns operation. This token indicates the position of a campaign in the list of campaigns.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The maximum number of results to include in response to a single call to the ListDomainDeliverabilityCampaigns operation. If the number of results is larger than the number that you specify in this parameter, the response includes a NextToken element, which you can use to obtain additional results.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    Retrieve deliverability data for all the campaigns that used a specific domain to send email during a specified time range. This data is available for a domain only if you enabled the Deliverability dashboard.

    " + }, + "ListDomainDeliverabilityCampaignsResponse":{ + "type":"structure", + "required":["DomainDeliverabilityCampaigns"], + "members":{ + "DomainDeliverabilityCampaigns":{ + "shape":"DomainDeliverabilityCampaignList", + "documentation":"

    An array of responses, one for each campaign that used the domain to send email during the specified time range.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that’s returned from a previous call to the ListDomainDeliverabilityCampaigns operation. This token indicates the position of the campaign in the list of campaigns.

    " + } + }, + "documentation":"

    An array of objects that provide deliverability data for all the campaigns that used a specific domain to send email during a specified time range. This data is available for a domain only if you enabled the Deliverability dashboard for the domain.

    " + }, + "ListEmailIdentitiesRequest":{ + "type":"structure", + "members":{ + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token returned from a previous call to ListEmailIdentities to indicate the position in the list of identities.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The number of results to show in a single call to ListEmailIdentities. If the number of results is larger than the number you specified in this parameter, then the response includes a NextToken element, which you can use to obtain additional results.

    The value you specify has to be at least 0, and can be no more than 1000.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    A request to list all of the email identities associated with your AWS account. This list includes identities that you've already verified, identities that are unverified, and identities that were verified in the past, but are no longer verified.

    " + }, + "ListEmailIdentitiesResponse":{ + "type":"structure", + "members":{ + "EmailIdentities":{ + "shape":"IdentityInfoList", + "documentation":"

    An array that includes all of the email identities associated with your AWS account.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that indicates that there are additional configuration sets to list. To view additional configuration sets, issue another request to ListEmailIdentities, and pass this token in the NextToken parameter.

    " + } + }, + "documentation":"

    A list of all of the identities that you've attempted to verify, regardless of whether or not those identities were successfully verified.

    " + }, + "ListOfDedicatedIpPools":{ + "type":"list", + "member":{"shape":"PoolName"}, + "documentation":"

    A list of dedicated IP pools that are associated with your AWS account.

    " + }, + "ListSuppressedDestinationsRequest":{ + "type":"structure", + "members":{ + "Reasons":{ + "shape":"SuppressionListReasons", + "documentation":"

    The factors that caused the email address to be added to .

    ", + "location":"querystring", + "locationName":"Reason" + }, + "StartDate":{ + "shape":"Timestamp", + "documentation":"

    Used to filter the list of suppressed email destinations so that it only includes addresses that were added to the list after a specific date. The date that you specify should be in Unix time format.

    ", + "location":"querystring", + "locationName":"StartDate" + }, + "EndDate":{ + "shape":"Timestamp", + "documentation":"

    Used to filter the list of suppressed email destinations so that it only includes addresses that were added to the list before a specific date. The date that you specify should be in Unix time format.

    ", + "location":"querystring", + "locationName":"EndDate" + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token returned from a previous call to ListSuppressedDestinations to indicate the position in the list of suppressed email addresses.

    ", + "location":"querystring", + "locationName":"NextToken" + }, + "PageSize":{ + "shape":"MaxItems", + "documentation":"

    The number of results to show in a single call to ListSuppressedDestinations. If the number of results is larger than the number you specified in this parameter, then the response includes a NextToken element, which you can use to obtain additional results.

    ", + "location":"querystring", + "locationName":"PageSize" + } + }, + "documentation":"

    A request to obtain a list of email destinations that are on the suppression list for your account.

    " + }, + "ListSuppressedDestinationsResponse":{ + "type":"structure", + "members":{ + "SuppressedDestinationSummaries":{ + "shape":"SuppressedDestinationSummaries", + "documentation":"

    A list of summaries, each containing a summary for a suppressed email destination.

    " + }, + "NextToken":{ + "shape":"NextToken", + "documentation":"

    A token that indicates that there are additional email addresses on the suppression list for your account. To view additional suppressed addresses, issue another request to ListSuppressedDestinations, and pass this token in the NextToken parameter.

    " + } + }, + "documentation":"

    A list of suppressed email addresses.

    " + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the resource that you want to retrieve tag information for.

    ", + "location":"querystring", + "locationName":"ResourceArn" + } + } + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "required":["Tags"], + "members":{ + "Tags":{ + "shape":"TagList", + "documentation":"

    An array that lists all the tags that are associated with the resource. Each tag consists of a required tag key (Key) and an associated tag value (Value)

    " + } + } + }, + "MailFromAttributes":{ + "type":"structure", + "required":[ + "MailFromDomain", + "MailFromDomainStatus", + "BehaviorOnMxFailure" + ], + "members":{ + "MailFromDomain":{ + "shape":"MailFromDomainName", + "documentation":"

    The name of a domain that an email identity uses as a custom MAIL FROM domain.

    " + }, + "MailFromDomainStatus":{ + "shape":"MailFromDomainStatus", + "documentation":"

    The status of the MAIL FROM domain. This status can have the following values:

    • PENDING – Amazon SES hasn't started searching for the MX record yet.

    • SUCCESS – Amazon SES detected the required MX record for the MAIL FROM domain.

    • FAILED – Amazon SES can't find the required MX record, or the record no longer exists.

    • TEMPORARY_FAILURE – A temporary issue occurred, which prevented Amazon SES from determining the status of the MAIL FROM domain.

    " + }, + "BehaviorOnMxFailure":{ + "shape":"BehaviorOnMxFailure", + "documentation":"

    The action that you want to take if the required MX record can't be found when you send an email. When you set this value to UseDefaultValue, the mail is sent using amazonses.com as the MAIL FROM domain. When you set this value to RejectMessage, the Amazon SES API v2 returns a MailFromDomainNotVerified error, and doesn't attempt to deliver the email.

    These behaviors are taken when the custom MAIL FROM domain configuration is in the Pending, Failed, and TemporaryFailure states.

    " + } + }, + "documentation":"

    A list of attributes that are associated with a MAIL FROM domain.

    " + }, + "MailFromDomainName":{ + "type":"string", + "documentation":"

    The domain that you want to use as a MAIL FROM domain.

    " + }, + "MailFromDomainNotVerifiedException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The message can't be sent because the sending domain isn't verified.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "MailFromDomainStatus":{ + "type":"string", + "documentation":"

    The status of the MAIL FROM domain. This status can have the following values:

    • PENDING – Amazon SES hasn't started searching for the MX record yet.

    • SUCCESS – Amazon SES detected the required MX record for the MAIL FROM domain.

    • FAILED – Amazon SES can't find the required MX record, or the record no longer exists.

    • TEMPORARY_FAILURE – A temporary issue occurred, which prevented Amazon SES from determining the status of the MAIL FROM domain.

    ", + "enum":[ + "PENDING", + "SUCCESS", + "FAILED", + "TEMPORARY_FAILURE" + ] + }, + "Max24HourSend":{"type":"double"}, + "MaxItems":{"type":"integer"}, + "MaxSendRate":{"type":"double"}, + "Message":{ + "type":"structure", + "required":[ + "Subject", + "Body" + ], + "members":{ + "Subject":{ + "shape":"Content", + "documentation":"

    The subject line of the email. The subject line can only contain 7-bit ASCII characters. However, you can specify non-ASCII characters in the subject line by using encoded-word syntax, as described in RFC 2047.

    " + }, + "Body":{ + "shape":"Body", + "documentation":"

    The body of the message. You can specify an HTML version of the message, a text-only version of the message, or both.

    " + } + }, + "documentation":"

    Represents the email message that you're sending. The Message object consists of a subject line and a message body.

    " + }, + "MessageContent":{ + "type":"string", + "documentation":"

    The body of an email message.

    " + }, + "MessageData":{"type":"string"}, + "MessageRejected":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The message can't be sent because it contains invalid content.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "MessageTag":{ + "type":"structure", + "required":[ + "Name", + "Value" + ], + "members":{ + "Name":{ + "shape":"MessageTagName", + "documentation":"

    The name of the message tag. The message tag name has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + }, + "Value":{ + "shape":"MessageTagValue", + "documentation":"

    The value of the message tag. The message tag value has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + } + }, + "documentation":"

    Contains the name and value of a tag that you apply to an email. You can use message tags when you publish email sending events.

    " + }, + "MessageTagList":{ + "type":"list", + "member":{"shape":"MessageTag"}, + "documentation":"

    A list of message tags.

    " + }, + "MessageTagName":{ + "type":"string", + "documentation":"

    The name of the message tag. The message tag name has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + }, + "MessageTagValue":{ + "type":"string", + "documentation":"

    The value of the message tag. The message tag value has to meet the following criteria:

    • It can only contain ASCII letters (a–z, A–Z), numbers (0–9), underscores (_), or dashes (-).

    • It can contain no more than 256 characters.

    " + }, + "NextToken":{"type":"string"}, + "NotFoundException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The resource you attempted to access doesn't exist.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "OutboundMessageId":{"type":"string"}, + "OverallVolume":{ + "type":"structure", + "members":{ + "VolumeStatistics":{ + "shape":"VolumeStatistics", + "documentation":"

    An object that contains information about the numbers of messages that arrived in recipients' inboxes and junk mail folders.

    " + }, + "ReadRatePercent":{ + "shape":"Percentage", + "documentation":"

    The percentage of emails that were sent from the domain that were read by their recipients.

    " + }, + "DomainIspPlacements":{ + "shape":"DomainIspPlacements", + "documentation":"

    An object that contains inbox and junk mail placement metrics for individual email providers.

    " + } + }, + "documentation":"

    An object that contains information about email that was sent from the selected domain.

    " + }, + "Percentage":{ + "type":"double", + "documentation":"

    An object that contains information about inbox placement percentages.

    " + }, + "Percentage100Wrapper":{"type":"integer"}, + "PinpointDestination":{ + "type":"structure", + "members":{ + "ApplicationArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the Amazon Pinpoint project that you want to send email events to.

    " + } + }, + "documentation":"

    An object that defines an Amazon Pinpoint project destination for email events. You can send email event data to a Amazon Pinpoint project to view metrics using the Transactional Messaging dashboards that are built in to Amazon Pinpoint. For more information, see Transactional Messaging Charts in the Amazon Pinpoint User Guide.

    " + }, + "PlacementStatistics":{ + "type":"structure", + "members":{ + "InboxPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of emails that arrived in recipients' inboxes during the predictive inbox placement test.

    " + }, + "SpamPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of emails that arrived in recipients' spam or junk mail folders during the predictive inbox placement test.

    " + }, + "MissingPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of emails that didn't arrive in recipients' inboxes at all during the predictive inbox placement test.

    " + }, + "SpfPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of emails that were authenticated by using Sender Policy Framework (SPF) during the predictive inbox placement test.

    " + }, + "DkimPercentage":{ + "shape":"Percentage", + "documentation":"

    The percentage of emails that were authenticated by using DomainKeys Identified Mail (DKIM) during the predictive inbox placement test.

    " + } + }, + "documentation":"

    An object that contains inbox placement data for an email provider.

    " + }, + "PoolName":{ + "type":"string", + "documentation":"

    The name of a dedicated IP pool.

    " + }, + "PrivateKey":{ + "type":"string", + "max":20480, + "min":1, + "pattern":"^[a-zA-Z0-9+\\/]+={0,2}$", + "sensitive":true + }, + "PutAccountDedicatedIpWarmupAttributesRequest":{ + "type":"structure", + "members":{ + "AutoWarmupEnabled":{ + "shape":"Enabled", + "documentation":"

    Enables or disables the automatic warm-up feature for dedicated IP addresses that are associated with your Amazon SES account in the current AWS Region. Set to true to enable the automatic warm-up feature, or set to false to disable it.

    " + } + }, + "documentation":"

    A request to enable or disable the automatic IP address warm-up feature.

    " + }, + "PutAccountDedicatedIpWarmupAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutAccountSendingAttributesRequest":{ + "type":"structure", + "members":{ + "SendingEnabled":{ + "shape":"Enabled", + "documentation":"

    Enables or disables your account's ability to send email. Set to true to enable email sending, or set to false to disable email sending.

    If AWS paused your account's ability to send email, you can't use this operation to resume your account's ability to send email.

    " + } + }, + "documentation":"

    A request to change the ability of your account to send email.

    " + }, + "PutAccountSendingAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutAccountSuppressionAttributesRequest":{ + "type":"structure", + "members":{ + "SuppressedReasons":{ + "shape":"SuppressionListReasons", + "documentation":"

    A list that contains the reasons that email addresses will be automatically added to the suppression list for your account. This list can contain any or all of the following:

    • COMPLAINT – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a complaint.

    • BOUNCE – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a hard bounce.

    " + } + }, + "documentation":"

    A request to change your account's suppression preferences.

    " + }, + "PutAccountSuppressionAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutConfigurationSetDeliveryOptionsRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to associate with a dedicated IP pool.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "TlsPolicy":{ + "shape":"TlsPolicy", + "documentation":"

    Specifies whether messages that use the configuration set are required to use Transport Layer Security (TLS). If the value is Require, messages are only delivered if a TLS connection can be established. If the value is Optional, messages can be delivered in plain text if a TLS connection can't be established.

    " + }, + "SendingPoolName":{ + "shape":"SendingPoolName", + "documentation":"

    The name of the dedicated IP pool that you want to associate with the configuration set.

    " + } + }, + "documentation":"

    A request to associate a configuration set with a dedicated IP pool.

    " + }, + "PutConfigurationSetDeliveryOptionsResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutConfigurationSetReputationOptionsRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to enable or disable reputation metric tracking for.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "ReputationMetricsEnabled":{ + "shape":"Enabled", + "documentation":"

    If true, tracking of reputation metrics is enabled for the configuration set. If false, tracking of reputation metrics is disabled for the configuration set.

    " + } + }, + "documentation":"

    A request to enable or disable tracking of reputation metrics for a configuration set.

    " + }, + "PutConfigurationSetReputationOptionsResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutConfigurationSetSendingOptionsRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to enable or disable email sending for.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "SendingEnabled":{ + "shape":"Enabled", + "documentation":"

    If true, email sending is enabled for the configuration set. If false, email sending is disabled for the configuration set.

    " + } + }, + "documentation":"

    A request to enable or disable the ability of Amazon SES to send emails that use a specific configuration set.

    " + }, + "PutConfigurationSetSendingOptionsResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutConfigurationSetSuppressionOptionsRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to change the suppression list preferences for.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "SuppressedReasons":{ + "shape":"SuppressionListReasons", + "documentation":"

    A list that contains the reasons that email addresses are automatically added to the suppression list for your account. This list can contain any or all of the following:

    • COMPLAINT – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a complaint.

    • BOUNCE – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a hard bounce.

    " + } + }, + "documentation":"

    A request to change the account suppression list preferences for a specific configuration set.

    " + }, + "PutConfigurationSetSuppressionOptionsResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutConfigurationSetTrackingOptionsRequest":{ + "type":"structure", + "required":["ConfigurationSetName"], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to add a custom tracking domain to.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "CustomRedirectDomain":{ + "shape":"CustomRedirectDomain", + "documentation":"

    The domain that you want to use to track open and click events.

    " + } + }, + "documentation":"

    A request to add a custom domain for tracking open and click events to a configuration set.

    " + }, + "PutConfigurationSetTrackingOptionsResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutDedicatedIpInPoolRequest":{ + "type":"structure", + "required":[ + "Ip", + "DestinationPoolName" + ], + "members":{ + "Ip":{ + "shape":"Ip", + "documentation":"

    The IP address that you want to move to the dedicated IP pool. The value you specify has to be a dedicated IP address that's associated with your AWS account.

    ", + "location":"uri", + "locationName":"IP" + }, + "DestinationPoolName":{ + "shape":"PoolName", + "documentation":"

    The name of the IP pool that you want to add the dedicated IP address to. You have to specify an IP pool that already exists.

    " + } + }, + "documentation":"

    A request to move a dedicated IP address to a dedicated IP pool.

    " + }, + "PutDedicatedIpInPoolResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutDedicatedIpWarmupAttributesRequest":{ + "type":"structure", + "required":[ + "Ip", + "WarmupPercentage" + ], + "members":{ + "Ip":{ + "shape":"Ip", + "documentation":"

    The dedicated IP address that you want to update the warm-up attributes for.

    ", + "location":"uri", + "locationName":"IP" + }, + "WarmupPercentage":{ + "shape":"Percentage100Wrapper", + "documentation":"

    The warm-up percentage that you want to associate with the dedicated IP address.

    " + } + }, + "documentation":"

    A request to change the warm-up attributes for a dedicated IP address. This operation is useful when you want to resume the warm-up process for an existing IP address.

    " + }, + "PutDedicatedIpWarmupAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutDeliverabilityDashboardOptionRequest":{ + "type":"structure", + "required":["DashboardEnabled"], + "members":{ + "DashboardEnabled":{ + "shape":"Enabled", + "documentation":"

    Specifies whether to enable the Deliverability dashboard. To enable the dashboard, set this value to true.

    " + }, + "SubscribedDomains":{ + "shape":"DomainDeliverabilityTrackingOptions", + "documentation":"

    An array of objects, one for each verified domain that you use to send email and enabled the Deliverability dashboard for.

    " + } + }, + "documentation":"

    Enable or disable the Deliverability dashboard. When you enable the Deliverability dashboard, you gain access to reputation, deliverability, and other metrics for the domains that you use to send email using Amazon SES API v2. You also gain the ability to perform predictive inbox placement tests.

    When you use the Deliverability dashboard, you pay a monthly subscription charge, in addition to any other fees that you accrue by using Amazon SES and other AWS services. For more information about the features and cost of a Deliverability dashboard subscription, see Amazon Pinpoint Pricing.

    " + }, + "PutDeliverabilityDashboardOptionResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    A response that indicates whether the Deliverability dashboard is enabled.

    " + }, + "PutEmailIdentityDkimAttributesRequest":{ + "type":"structure", + "required":["EmailIdentity"], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The email identity that you want to change the DKIM settings for.

    ", + "location":"uri", + "locationName":"EmailIdentity" + }, + "SigningEnabled":{ + "shape":"Enabled", + "documentation":"

    Sets the DKIM signing configuration for the identity.

    When you set this value true, then the messages that are sent from the identity are signed using DKIM. If you set this value to false, your messages are sent without DKIM signing.

    " + } + }, + "documentation":"

    A request to enable or disable DKIM signing of email that you send from an email identity.

    " + }, + "PutEmailIdentityDkimAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutEmailIdentityDkimSigningAttributesRequest":{ + "type":"structure", + "required":[ + "EmailIdentity", + "SigningAttributesOrigin" + ], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The email identity that you want to configure DKIM for.

    ", + "location":"uri", + "locationName":"EmailIdentity" + }, + "SigningAttributesOrigin":{ + "shape":"DkimSigningAttributesOrigin", + "documentation":"

    The method that you want to use to configure DKIM for the identity. There are two possible values:

    • AWS_SES – Configure DKIM for the identity by using Easy DKIM.

    • EXTERNAL – Configure DKIM for the identity by using Bring Your Own DKIM (BYODKIM).

    " + }, + "SigningAttributes":{ + "shape":"DkimSigningAttributes", + "documentation":"

    An object that contains information about the private key and selector that you want to use to configure DKIM for the identity. This object is only required if you want to configure Bring Your Own DKIM (BYODKIM) for the identity.

    " + } + }, + "documentation":"

    A request to change the DKIM attributes for an email identity.

    " + }, + "PutEmailIdentityDkimSigningAttributesResponse":{ + "type":"structure", + "members":{ + "DkimStatus":{ + "shape":"DkimStatus", + "documentation":"

    The DKIM authentication status of the identity. Amazon SES determines the authentication status by searching for specific records in the DNS configuration for your domain. If you used Easy DKIM to set up DKIM authentication, Amazon SES tries to find three unique CNAME records in the DNS configuration for your domain.

    If you provided a public key to perform DKIM authentication, Amazon SES tries to find a TXT record that uses the selector that you specified. The value of the TXT record must be a public key that's paired with the private key that you specified in the process of creating the identity.

    The status can be one of the following:

    • PENDING – The verification process was initiated, but Amazon SES hasn't yet detected the DKIM records in the DNS configuration for the domain.

    • SUCCESS – The verification process completed successfully.

    • FAILED – The verification process failed. This typically occurs when Amazon SES fails to find the DKIM records in the DNS configuration of the domain.

    • TEMPORARY_FAILURE – A temporary issue is preventing Amazon SES from determining the DKIM authentication status of the domain.

    • NOT_STARTED – The DKIM verification process hasn't been initiated for the domain.

    " + }, + "DkimTokens":{ + "shape":"DnsTokenList", + "documentation":"

    If you used Easy DKIM to configure DKIM authentication for the domain, then this object contains a set of unique strings that you use to create a set of CNAME records that you add to the DNS configuration for your domain. When Amazon SES detects these records in the DNS configuration for your domain, the DKIM authentication process is complete.

    If you configured DKIM authentication for the domain by providing your own public-private key pair, then this object contains the selector that's associated with your public key.

    Regardless of the DKIM authentication method you use, Amazon SES searches for the appropriate records in the DNS configuration of the domain for up to 72 hours.

    " + } + }, + "documentation":"

    If the action is successful, the service sends back an HTTP 200 response.

    The following data is returned in JSON format by the service.

    " + }, + "PutEmailIdentityFeedbackAttributesRequest":{ + "type":"structure", + "required":["EmailIdentity"], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The email identity that you want to configure bounce and complaint feedback forwarding for.

    ", + "location":"uri", + "locationName":"EmailIdentity" + }, + "EmailForwardingEnabled":{ + "shape":"Enabled", + "documentation":"

    Sets the feedback forwarding configuration for the identity.

    If the value is true, you receive email notifications when bounce or complaint events occur. These notifications are sent to the address that you specified in the Return-Path header of the original email.

    You're required to have a method of tracking bounces and complaints. If you haven't set up another mechanism for receiving bounce or complaint notifications (for example, by setting up an event destination), you receive an email notification when these events occur (even if this setting is disabled).

    " + } + }, + "documentation":"

    A request to set the attributes that control how bounce and complaint events are processed.

    " + }, + "PutEmailIdentityFeedbackAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutEmailIdentityMailFromAttributesRequest":{ + "type":"structure", + "required":["EmailIdentity"], + "members":{ + "EmailIdentity":{ + "shape":"Identity", + "documentation":"

    The verified email identity that you want to set up the custom MAIL FROM domain for.

    ", + "location":"uri", + "locationName":"EmailIdentity" + }, + "MailFromDomain":{ + "shape":"MailFromDomainName", + "documentation":"

    The custom MAIL FROM domain that you want the verified identity to use. The MAIL FROM domain must meet the following criteria:

    • It has to be a subdomain of the verified identity.

    • It can't be used to receive email.

    • It can't be used in a \"From\" address if the MAIL FROM domain is a destination for feedback forwarding emails.

    " + }, + "BehaviorOnMxFailure":{ + "shape":"BehaviorOnMxFailure", + "documentation":"

    The action that you want to take if the required MX record isn't found when you send an email. When you set this value to UseDefaultValue, the mail is sent using amazonses.com as the MAIL FROM domain. When you set this value to RejectMessage, the Amazon SES API v2 returns a MailFromDomainNotVerified error, and doesn't attempt to deliver the email.

    These behaviors are taken when the custom MAIL FROM domain configuration is in the Pending, Failed, and TemporaryFailure states.

    " + } + }, + "documentation":"

    A request to configure the custom MAIL FROM domain for a verified identity.

    " + }, + "PutEmailIdentityMailFromAttributesResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "PutSuppressedDestinationRequest":{ + "type":"structure", + "required":[ + "EmailAddress", + "Reason" + ], + "members":{ + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The email address that should be added to the suppression list for your account.

    " + }, + "Reason":{ + "shape":"SuppressionListReason", + "documentation":"

    The factors that should cause the email address to be added to the suppression list for your account.

    " + } + }, + "documentation":"

    A request to add an email destination to the suppression list for your account.

    " + }, + "PutSuppressedDestinationResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "RawMessage":{ + "type":"structure", + "required":["Data"], + "members":{ + "Data":{ + "shape":"RawMessageData", + "documentation":"

    The raw email message. The message has to meet the following criteria:

    • The message has to contain a header and a body, separated by one blank line.

    • All of the required header fields must be present in the message.

    • Each part of a multipart MIME message must be formatted properly.

    • Attachments must be in a file format that the Amazon SES supports.

    • The entire message must be Base64 encoded.

    • If any of the MIME parts in your message contain content that is outside of the 7-bit ASCII character range, you should encode that content to ensure that recipients' email clients render the message properly.

    • The length of any single line of text in the message can't exceed 1,000 characters. This restriction is defined in RFC 5321.

    " + } + }, + "documentation":"

    Represents the raw content of an email message.

    " + }, + "RawMessageData":{ + "type":"blob", + "documentation":"

    The raw email message. The message has to meet the following criteria:

    • The message has to contain a header and a body, separated by one blank line.

    • All of the required header fields must be present in the message.

    • Each part of a multipart MIME message must be formatted properly.

    • Attachments must be in a file format that the Amazon SES API v2 supports.

    • The entire message must be Base64 encoded.

    • If any of the MIME parts in your message contain content that is outside of the 7-bit ASCII character range, you should encode that content to ensure that recipients' email clients render the message properly.

    • The length of any single line of text in the message can't exceed 1,000 characters. This restriction is defined in RFC 5321.

    " + }, + "RblName":{ + "type":"string", + "documentation":"

    The name of a blacklist that an IP address was found on.

    " + }, + "ReportId":{ + "type":"string", + "documentation":"

    A unique string that identifies a Deliverability dashboard report.

    " + }, + "ReportName":{ + "type":"string", + "documentation":"

    A name that helps you identify a report generated by the Deliverability dashboard.

    " + }, + "ReputationOptions":{ + "type":"structure", + "members":{ + "ReputationMetricsEnabled":{ + "shape":"Enabled", + "documentation":"

    If true, tracking of reputation metrics is enabled for the configuration set. If false, tracking of reputation metrics is disabled for the configuration set.

    " + }, + "LastFreshStart":{ + "shape":"LastFreshStart", + "documentation":"

    The date and time (in Unix time) when the reputation metrics were last given a fresh start. When your account is given a fresh start, your reputation metrics are calculated starting from the date of the fresh start.

    " + } + }, + "documentation":"

    Enable or disable collection of reputation metrics for emails that you send using this configuration set in the current AWS Region.

    " + }, + "Selector":{ + "type":"string", + "max":63, + "min":1, + "pattern":"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9]))$" + }, + "SendEmailRequest":{ + "type":"structure", + "required":[ + "Destination", + "Content" + ], + "members":{ + "FromEmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The email address that you want to use as the \"From\" address for the email. The address that you specify has to be verified.

    " + }, + "Destination":{ + "shape":"Destination", + "documentation":"

    An object that contains the recipients of the email message.

    " + }, + "ReplyToAddresses":{ + "shape":"EmailAddressList", + "documentation":"

    The \"Reply-to\" email addresses for the message. When the recipient replies to the message, each Reply-to address receives the reply.

    " + }, + "FeedbackForwardingEmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The address that you want bounce and complaint notifications to be sent to.

    " + }, + "Content":{ + "shape":"EmailContent", + "documentation":"

    An object that contains the body of the message. You can send either a Simple message or a Raw message.

    " + }, + "EmailTags":{ + "shape":"MessageTagList", + "documentation":"

    A list of tags, in the form of name/value pairs, to apply to an email that you send using the SendEmail operation. Tags correspond to characteristics of the email that you define, so that you can publish email sending events.

    " + }, + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that you want to use when sending the email.

    " + } + }, + "documentation":"

    A request to send an email message.

    " + }, + "SendEmailResponse":{ + "type":"structure", + "members":{ + "MessageId":{ + "shape":"OutboundMessageId", + "documentation":"

    A unique identifier for the message that is generated when the message is accepted.

    It's possible for Amazon SES to accept a message without sending it. This can happen when the message that you're trying to send has an attachment contains a virus, or when you send a templated email that contains invalid personalization content, for example.

    " + } + }, + "documentation":"

    A unique message ID that you receive when an email is accepted for sending.

    " + }, + "SendQuota":{ + "type":"structure", + "members":{ + "Max24HourSend":{ + "shape":"Max24HourSend", + "documentation":"

    The maximum number of emails that you can send in the current AWS Region over a 24-hour period. This value is also called your sending quota.

    " + }, + "MaxSendRate":{ + "shape":"MaxSendRate", + "documentation":"

    The maximum number of emails that you can send per second in the current AWS Region. This value is also called your maximum sending rate or your maximum TPS (transactions per second) rate.

    " + }, + "SentLast24Hours":{ + "shape":"SentLast24Hours", + "documentation":"

    The number of emails sent from your Amazon SES account in the current AWS Region over the past 24 hours.

    " + } + }, + "documentation":"

    An object that contains information about the per-day and per-second sending limits for your Amazon SES account in the current AWS Region.

    " + }, + "SendingOptions":{ + "type":"structure", + "members":{ + "SendingEnabled":{ + "shape":"Enabled", + "documentation":"

    If true, email sending is enabled for the configuration set. If false, email sending is disabled for the configuration set.

    " + } + }, + "documentation":"

    Used to enable or disable email sending for messages that use this configuration set in the current AWS Region.

    " + }, + "SendingPausedException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    The message can't be sent because the account's ability to send email is currently paused.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "SendingPoolName":{ + "type":"string", + "documentation":"

    The name of the dedicated IP pool that you want to associate with the configuration set.

    " + }, + "SentLast24Hours":{"type":"double"}, + "SnsDestination":{ + "type":"structure", + "required":["TopicArn"], + "members":{ + "TopicArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the Amazon SNS topic that you want to publish email events to. For more information about Amazon SNS topics, see the Amazon SNS Developer Guide.

    " + } + }, + "documentation":"

    An object that defines an Amazon SNS destination for email events. You can use Amazon SNS to send notification when certain email events occur.

    " + }, + "Subject":{"type":"string"}, + "SuppressedDestination":{ + "type":"structure", + "required":[ + "EmailAddress", + "Reason", + "LastUpdateTime" + ], + "members":{ + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The email address that is on the suppression list for your account.

    " + }, + "Reason":{ + "shape":"SuppressionListReason", + "documentation":"

    The reason that the address was added to the suppression list for your account.

    " + }, + "LastUpdateTime":{ + "shape":"Timestamp", + "documentation":"

    The date and time when the suppressed destination was last updated, shown in Unix time format.

    " + }, + "Attributes":{ + "shape":"SuppressedDestinationAttributes", + "documentation":"

    An optional value that can contain additional information about the reasons that the address was added to the suppression list for your account.

    " + } + }, + "documentation":"

    An object that contains information about an email address that is on the suppression list for your account.

    " + }, + "SuppressedDestinationAttributes":{ + "type":"structure", + "members":{ + "MessageId":{ + "shape":"OutboundMessageId", + "documentation":"

    The unique identifier of the email message that caused the email address to be added to the suppression list for your account.

    " + }, + "FeedbackId":{ + "shape":"FeedbackId", + "documentation":"

    A unique identifier that's generated when an email address is added to the suppression list for your account.

    " + } + }, + "documentation":"

    An object that contains additional attributes that are related an email address that is on the suppression list for your account.

    " + }, + "SuppressedDestinationSummaries":{ + "type":"list", + "member":{"shape":"SuppressedDestinationSummary"} + }, + "SuppressedDestinationSummary":{ + "type":"structure", + "required":[ + "EmailAddress", + "Reason", + "LastUpdateTime" + ], + "members":{ + "EmailAddress":{ + "shape":"EmailAddress", + "documentation":"

    The email address that's on the suppression list for your account.

    " + }, + "Reason":{ + "shape":"SuppressionListReason", + "documentation":"

    The reason that the address was added to the suppression list for your account.

    " + }, + "LastUpdateTime":{ + "shape":"Timestamp", + "documentation":"

    The date and time when the suppressed destination was last updated, shown in Unix time format.

    " + } + }, + "documentation":"

    A summary that describes the suppressed email address.

    " + }, + "SuppressionAttributes":{ + "type":"structure", + "members":{ + "SuppressedReasons":{ + "shape":"SuppressionListReasons", + "documentation":"

    A list that contains the reasons that email addresses will be automatically added to the suppression list for your account. This list can contain any or all of the following:

    • COMPLAINT – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a complaint.

    • BOUNCE – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a hard bounce.

    " + } + }, + "documentation":"

    An object that contains information about the email address suppression preferences for your account in the current AWS Region.

    " + }, + "SuppressionListReason":{ + "type":"string", + "documentation":"

    The reason that the address was added to the suppression list for your account. The value can be one of the following:

    • COMPLAINT – Amazon SES added an email address to the suppression list for your account because a message sent to that address results in a complaint.

    • BOUNCE – Amazon SES added an email address to the suppression list for your account because a message sent to that address results in a hard bounce.

    ", + "enum":[ + "BOUNCE", + "COMPLAINT" + ] + }, + "SuppressionListReasons":{ + "type":"list", + "member":{"shape":"SuppressionListReason"} + }, + "SuppressionOptions":{ + "type":"structure", + "members":{ + "SuppressedReasons":{ + "shape":"SuppressionListReasons", + "documentation":"

    A list that contains the reasons that email addresses are automatically added to the suppression list for your account. This list can contain any or all of the following:

    • COMPLAINT – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a complaint.

    • BOUNCE – Amazon SES adds an email address to the suppression list for your account when a message sent to that address results in a hard bounce.

    " + } + }, + "documentation":"

    An object that contains information about the suppression list preferences for your account.

    " + }, + "Tag":{ + "type":"structure", + "required":[ + "Key", + "Value" + ], + "members":{ + "Key":{ + "shape":"TagKey", + "documentation":"

    One part of a key-value pair that defines a tag. The maximum length of a tag key is 128 characters. The minimum length is 1 character.

    " + }, + "Value":{ + "shape":"TagValue", + "documentation":"

    The optional part of a key-value pair that defines a tag. The maximum length of a tag value is 256 characters. The minimum length is 0 characters. If you don't want a resource to have a specific tag value, don't specify a value for this parameter. If you don't specify a value, Amazon SES sets the value to an empty string.

    " + } + }, + "documentation":"

    An object that defines the tags that are associated with a resource. A tag is a label that you optionally define and associate with a resource. Tags can help you categorize and manage resources in different ways, such as by purpose, owner, environment, or other criteria. A resource can have as many as 50 tags.

    Each tag consists of a required tag key and an associated tag value, both of which you define. A tag key is a general label that acts as a category for a more specific tag value. A tag value acts as a descriptor within a tag key. A tag key can contain as many as 128 characters. A tag value can contain as many as 256 characters. The characters can be Unicode letters, digits, white space, or one of the following symbols: _ . : / = + -. The following additional restrictions apply to tags:

    • Tag keys and values are case sensitive.

    • For each associated resource, each tag key must be unique and it can have only one value.

    • The aws: prefix is reserved for use by AWS; you can’t use it in any tag keys or values that you define. In addition, you can't edit or remove tag keys or values that use this prefix. Tags that use this prefix don’t count against the limit of 50 tags per resource.

    • You can associate tags with public or shared resources, but the tags are available only for your AWS account, not any other accounts that share the resource. In addition, the tags are available only for resources that are located in the specified AWS Region for your AWS account.

    " + }, + "TagKey":{"type":"string"}, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"} + }, + "TagList":{ + "type":"list", + "member":{"shape":"Tag"} + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "Tags" + ], + "members":{ + "ResourceArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the resource that you want to add one or more tags to.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    A list of the tags that you want to add to the resource. A tag consists of a required tag key (Key) and an associated tag value (Value). The maximum length of a tag key is 128 characters. The maximum length of a tag value is 256 characters.

    " + } + } + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{"type":"string"}, + "Template":{ + "type":"structure", + "members":{ + "TemplateArn":{ + "shape":"TemplateArn", + "documentation":"

    The Amazon Resource Name (ARN) of the template.

    " + }, + "TemplateData":{ + "shape":"TemplateData", + "documentation":"

    An object that defines the values to use for message variables in the template. This object is a set of key-value pairs. Each key defines a message variable in the template. The corresponding value defines the value to use for that variable.

    " + } + }, + "documentation":"

    An object that defines the email template to use for an email message, and the values to use for any message variables in that template. An email template is a type of message template that contains content that you want to define, save, and reuse in email messages that you send.

    " + }, + "TemplateArn":{"type":"string"}, + "TemplateData":{ + "type":"string", + "max":262144 + }, + "Timestamp":{"type":"timestamp"}, + "TlsPolicy":{ + "type":"string", + "documentation":"

    Specifies whether messages that use the configuration set are required to use Transport Layer Security (TLS). If the value is Require, messages are only delivered if a TLS connection can be established. If the value is Optional, messages can be delivered in plain text if a TLS connection can't be established.

    ", + "enum":[ + "REQUIRE", + "OPTIONAL" + ] + }, + "TooManyRequestsException":{ + "type":"structure", + "members":{ + }, + "documentation":"

    Too many requests have been made to the operation.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "TrackingOptions":{ + "type":"structure", + "required":["CustomRedirectDomain"], + "members":{ + "CustomRedirectDomain":{ + "shape":"CustomRedirectDomain", + "documentation":"

    The domain that you want to use for tracking open and click events.

    " + } + }, + "documentation":"

    An object that defines the tracking options for a configuration set. When you use the Amazon SES API v2 to send an email, it contains an invisible image that's used to track when recipients open your email. If your email contains links, those links are changed slightly in order to track when recipients click them.

    These images and links include references to a domain operated by AWS. You can optionally configure the Amazon SES to use a domain that you operate for these images and links.

    " + }, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "TagKeys" + ], + "members":{ + "ResourceArn":{ + "shape":"AmazonResourceName", + "documentation":"

    The Amazon Resource Name (ARN) of the resource that you want to remove one or more tags from.

    ", + "location":"querystring", + "locationName":"ResourceArn" + }, + "TagKeys":{ + "shape":"TagKeyList", + "documentation":"

    The tags (tag keys) that you want to remove from the resource. When you specify a tag key, the action removes both that key and its associated tag value.

    To remove more than one tag from the resource, append the TagKeys parameter and argument for each additional tag to remove, separated by an ampersand. For example: /v2/email/tags?ResourceArn=ResourceArn&TagKeys=Key1&TagKeys=Key2

    ", + "location":"querystring", + "locationName":"TagKeys" + } + } + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "UpdateConfigurationSetEventDestinationRequest":{ + "type":"structure", + "required":[ + "ConfigurationSetName", + "EventDestinationName", + "EventDestination" + ], + "members":{ + "ConfigurationSetName":{ + "shape":"ConfigurationSetName", + "documentation":"

    The name of the configuration set that contains the event destination that you want to modify.

    ", + "location":"uri", + "locationName":"ConfigurationSetName" + }, + "EventDestinationName":{ + "shape":"EventDestinationName", + "documentation":"

    The name of the event destination that you want to modify.

    ", + "location":"uri", + "locationName":"EventDestinationName" + }, + "EventDestination":{ + "shape":"EventDestinationDefinition", + "documentation":"

    An object that defines the event destination.

    " + } + }, + "documentation":"

    A request to change the settings for an event destination for a configuration set.

    " + }, + "UpdateConfigurationSetEventDestinationResponse":{ + "type":"structure", + "members":{ + }, + "documentation":"

    An HTTP 200 response if the request succeeds, or an error message if the request fails.

    " + }, + "Volume":{ + "type":"long", + "documentation":"

    An object that contains information about inbox placement volume.

    " + }, + "VolumeStatistics":{ + "type":"structure", + "members":{ + "InboxRawCount":{ + "shape":"Volume", + "documentation":"

    The total number of emails that arrived in recipients' inboxes.

    " + }, + "SpamRawCount":{ + "shape":"Volume", + "documentation":"

    The total number of emails that arrived in recipients' spam or junk mail folders.

    " + }, + "ProjectedInbox":{ + "shape":"Volume", + "documentation":"

    An estimate of the percentage of emails sent from the current domain that will arrive in recipients' inboxes.

    " + }, + "ProjectedSpam":{ + "shape":"Volume", + "documentation":"

    An estimate of the percentage of emails sent from the current domain that will arrive in recipients' spam or junk mail folders.

    " + } + }, + "documentation":"

    An object that contains information about the amount of email that was delivered to recipients.

    " + }, + "WarmupStatus":{ + "type":"string", + "documentation":"

    The warmup status of a dedicated IP.

    ", + "enum":[ + "IN_PROGRESS", + "DONE" + ] + } + }, + "documentation":"Amazon SES API v2

    Welcome to the Amazon SES API v2 Reference. This guide provides information about the Amazon SES API v2, including supported operations, data types, parameters, and schemas.

    Amazon SES is an AWS service that you can use to send email messages to your customers.

    If you're new to Amazon SES API v2, you might find it helpful to also review the Amazon Simple Email Service Developer Guide. The Amazon SES Developer Guide provides information and code samples that demonstrate how to use Amazon SES API v2 features programmatically.

    The Amazon SES API v2 is available in several AWS Regions and it provides an endpoint for each of these Regions. For a list of all the Regions and endpoints where the API is currently available, see AWS Service Endpoints in the Amazon Web Services General Reference. To learn more about AWS Regions, see Managing AWS Regions in the Amazon Web Services General Reference.

    In each Region, AWS maintains multiple Availability Zones. These Availability Zones are physically isolated from each other, but are united by private, low-latency, high-throughput, and highly redundant network connections. These Availability Zones enable us to provide very high levels of availability and redundancy, while also minimizing latency. To learn more about the number of Availability Zones that are available in each Region, see AWS Global Infrastructure.

    " +} diff --git a/services/sfn/build.properties b/services/sfn/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/sfn/build.properties +++ b/services/sfn/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/sfn/pom.xml b/services/sfn/pom.xml index d9e5ed8d1418..e8f5feb0291c 100644 --- a/services/sfn/pom.xml +++ b/services/sfn/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + sso + AWS Java SDK :: Services :: SSO + The AWS Java SDK for SSO module holds the client classes that are used for + communicating with SSO. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.sso + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/sso/src/main/resources/codegen-resources/paginators-1.json b/services/sso/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..daaed6fe69df --- /dev/null +++ b/services/sso/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,16 @@ +{ + "pagination": { + "ListAccountRoles": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "roleList" + }, + "ListAccounts": { + "input_token": "nextToken", + "output_token": "nextToken", + "limit_key": "maxResults", + "result_key": "accountList" + } + } +} diff --git a/services/sso/src/main/resources/codegen-resources/service-2.json b/services/sso/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..84e4fa40f792 --- /dev/null +++ b/services/sso/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,346 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-06-10", + "endpointPrefix":"portal.sso", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"SSO", + "serviceFullName":"AWS Single Sign-On", + "serviceId":"SSO", + "signatureVersion":"v4", + "signingName":"awsssoportal", + "uid":"sso-2019-06-10" + }, + "operations":{ + "GetRoleCredentials":{ + "name":"GetRoleCredentials", + "http":{ + "method":"GET", + "requestUri":"/federation/credentials" + }, + "input":{"shape":"GetRoleCredentialsRequest"}, + "output":{"shape":"GetRoleCredentialsResponse"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"UnauthorizedException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Returns the STS short-term credentials for a given role name that is assigned to the user.

    ", + "authtype":"none" + }, + "ListAccountRoles":{ + "name":"ListAccountRoles", + "http":{ + "method":"GET", + "requestUri":"/assignment/roles" + }, + "input":{"shape":"ListAccountRolesRequest"}, + "output":{"shape":"ListAccountRolesResponse"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"UnauthorizedException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Lists all roles that are assigned to the user for a given AWS account.

    ", + "authtype":"none" + }, + "ListAccounts":{ + "name":"ListAccounts", + "http":{ + "method":"GET", + "requestUri":"/assignment/accounts" + }, + "input":{"shape":"ListAccountsRequest"}, + "output":{"shape":"ListAccountsResponse"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"UnauthorizedException"}, + {"shape":"TooManyRequestsException"}, + {"shape":"ResourceNotFoundException"} + ], + "documentation":"

    Lists all AWS accounts assigned to the user. These AWS accounts are assigned by the administrator of the account. For more information, see Assign User Access in the AWS SSO User Guide. This operation returns a paginated response.

    ", + "authtype":"none" + }, + "Logout":{ + "name":"Logout", + "http":{ + "method":"POST", + "requestUri":"/logout" + }, + "input":{"shape":"LogoutRequest"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"UnauthorizedException"}, + {"shape":"TooManyRequestsException"} + ], + "documentation":"

    Removes the client- and server-side session that is associated with the user.

    ", + "authtype":"none" + } + }, + "shapes":{ + "AccessKeyType":{"type":"string"}, + "AccessTokenType":{ + "type":"string", + "sensitive":true + }, + "AccountIdType":{"type":"string"}, + "AccountInfo":{ + "type":"structure", + "members":{ + "accountId":{ + "shape":"AccountIdType", + "documentation":"

    The identifier of the AWS account that is assigned to the user.

    " + }, + "accountName":{ + "shape":"AccountNameType", + "documentation":"

    The display name of the AWS account that is assigned to the user.

    " + }, + "emailAddress":{ + "shape":"EmailAddressType", + "documentation":"

    The email address of the AWS account that is assigned to the user.

    " + } + }, + "documentation":"

    Provides information about your AWS account.

    " + }, + "AccountListType":{ + "type":"list", + "member":{"shape":"AccountInfo"} + }, + "AccountNameType":{"type":"string"}, + "EmailAddressType":{ + "type":"string", + "max":254, + "min":1 + }, + "ErrorDescription":{"type":"string"}, + "ExpirationTimestampType":{"type":"long"}, + "GetRoleCredentialsRequest":{ + "type":"structure", + "required":[ + "roleName", + "accountId", + "accessToken" + ], + "members":{ + "roleName":{ + "shape":"RoleNameType", + "documentation":"

    The friendly name of the role that is assigned to the user.

    ", + "location":"querystring", + "locationName":"role_name" + }, + "accountId":{ + "shape":"AccountIdType", + "documentation":"

    The identifier for the AWS account that is assigned to the user.

    ", + "location":"querystring", + "locationName":"account_id" + }, + "accessToken":{ + "shape":"AccessTokenType", + "documentation":"

    The token issued by the CreateToken API call. For more information, see CreateToken in the AWS SSO OIDC API Reference Guide.

    ", + "location":"header", + "locationName":"x-amz-sso_bearer_token" + } + } + }, + "GetRoleCredentialsResponse":{ + "type":"structure", + "members":{ + "roleCredentials":{ + "shape":"RoleCredentials", + "documentation":"

    The credentials for the role that is assigned to the user.

    " + } + } + }, + "InvalidRequestException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that a problem occurred with the input to the request. For example, a required parameter might be missing or out of range.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ListAccountRolesRequest":{ + "type":"structure", + "required":[ + "accessToken", + "accountId" + ], + "members":{ + "nextToken":{ + "shape":"NextTokenType", + "documentation":"

    The page token from the previous response output when you request subsequent pages.

    ", + "location":"querystring", + "locationName":"next_token" + }, + "maxResults":{ + "shape":"MaxResultType", + "documentation":"

    The number of items that clients can request per page.

    ", + "location":"querystring", + "locationName":"max_result" + }, + "accessToken":{ + "shape":"AccessTokenType", + "documentation":"

    The token issued by the CreateToken API call. For more information, see CreateToken in the AWS SSO OIDC API Reference Guide.

    ", + "location":"header", + "locationName":"x-amz-sso_bearer_token" + }, + "accountId":{ + "shape":"AccountIdType", + "documentation":"

    The identifier for the AWS account that is assigned to the user.

    ", + "location":"querystring", + "locationName":"account_id" + } + } + }, + "ListAccountRolesResponse":{ + "type":"structure", + "members":{ + "nextToken":{ + "shape":"NextTokenType", + "documentation":"

    The page token client that is used to retrieve the list of accounts.

    " + }, + "roleList":{ + "shape":"RoleListType", + "documentation":"

    A paginated response with the list of roles and the next token if more results are available.

    " + } + } + }, + "ListAccountsRequest":{ + "type":"structure", + "required":["accessToken"], + "members":{ + "nextToken":{ + "shape":"NextTokenType", + "documentation":"

    (Optional) When requesting subsequent pages, this is the page token from the previous response output.

    ", + "location":"querystring", + "locationName":"next_token" + }, + "maxResults":{ + "shape":"MaxResultType", + "documentation":"

    This is the number of items clients can request per page.

    ", + "location":"querystring", + "locationName":"max_result" + }, + "accessToken":{ + "shape":"AccessTokenType", + "documentation":"

    The token issued by the CreateToken API call. For more information, see CreateToken in the AWS SSO OIDC API Reference Guide.

    ", + "location":"header", + "locationName":"x-amz-sso_bearer_token" + } + } + }, + "ListAccountsResponse":{ + "type":"structure", + "members":{ + "nextToken":{ + "shape":"NextTokenType", + "documentation":"

    The page token client that is used to retrieve the list of accounts.

    " + }, + "accountList":{ + "shape":"AccountListType", + "documentation":"

    A paginated response with the list of account information and the next token if more results are available.

    " + } + } + }, + "LogoutRequest":{ + "type":"structure", + "required":["accessToken"], + "members":{ + "accessToken":{ + "shape":"AccessTokenType", + "documentation":"

    The token issued by the CreateToken API call. For more information, see CreateToken in the AWS SSO OIDC API Reference Guide.

    ", + "location":"header", + "locationName":"x-amz-sso_bearer_token" + } + } + }, + "MaxResultType":{ + "type":"integer", + "box":true, + "max":100, + "min":1 + }, + "NextTokenType":{"type":"string"}, + "ResourceNotFoundException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorDescription"} + }, + "documentation":"

    The specified resource doesn't exist.

    ", + "error":{"httpStatusCode":404}, + "exception":true + }, + "RoleCredentials":{ + "type":"structure", + "members":{ + "accessKeyId":{ + "shape":"AccessKeyType", + "documentation":"

    The identifier used for the temporary security credentials. For more information, see Using Temporary Security Credentials to Request Access to AWS Resources in the AWS IAM User Guide.

    " + }, + "secretAccessKey":{ + "shape":"SecretAccessKeyType", + "documentation":"

    The key that is used to sign the request. For more information, see Using Temporary Security Credentials to Request Access to AWS Resources in the AWS IAM User Guide.

    " + }, + "sessionToken":{ + "shape":"SessionTokenType", + "documentation":"

    The token used for temporary credentials. For more information, see Using Temporary Security Credentials to Request Access to AWS Resources in the AWS IAM User Guide.

    " + }, + "expiration":{ + "shape":"ExpirationTimestampType", + "documentation":"

    The date on which temporary security credentials expire.

    " + } + }, + "documentation":"

    Provides information about the role credentials that are assigned to the user.

    " + }, + "RoleInfo":{ + "type":"structure", + "members":{ + "roleName":{ + "shape":"RoleNameType", + "documentation":"

    The friendly name of the role that is assigned to the user.

    " + }, + "accountId":{ + "shape":"AccountIdType", + "documentation":"

    The identifier of the AWS account assigned to the user.

    " + } + }, + "documentation":"

    Provides information about the role that is assigned to the user.

    " + }, + "RoleListType":{ + "type":"list", + "member":{"shape":"RoleInfo"} + }, + "RoleNameType":{"type":"string"}, + "SecretAccessKeyType":{ + "type":"string", + "sensitive":true + }, + "SessionTokenType":{ + "type":"string", + "sensitive":true + }, + "TooManyRequestsException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the request is being made too frequently and is more than what the server can handle.

    ", + "error":{"httpStatusCode":429}, + "exception":true + }, + "UnauthorizedException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the request is not authorized. This can happen due to an invalid access token in the request.

    ", + "error":{"httpStatusCode":401}, + "exception":true + } + }, + "documentation":"

    AWS Single Sign-On Portal is a web service that makes it easy for you to assign user access to AWS SSO resources such as the user portal. Users can get AWS account applications and roles assigned to them and get federated into the application.

    For general information about AWS SSO, see What is AWS Single Sign-On? in the AWS SSO User Guide.

    This API reference guide describes the AWS SSO Portal operations that you can call programatically and includes detailed information on data types and errors.

    AWS provides SDKs that consist of libraries and sample code for various programming languages and platforms, such as Java, Ruby, .Net, iOS, or Android. The SDKs provide a convenient way to create programmatic access to AWS SSO and other AWS services. For more information about the AWS SDKs, including how to download and install them, see Tools for Amazon Web Services.

    " +} diff --git a/services/ssooidc/pom.xml b/services/ssooidc/pom.xml new file mode 100644 index 000000000000..4ff0ccf65032 --- /dev/null +++ b/services/ssooidc/pom.xml @@ -0,0 +1,60 @@ + + + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + ssooidc + AWS Java SDK :: Services :: SSO OIDC + The AWS Java SDK for SSO OIDC module holds the client classes that are used for + communicating with SSO OIDC. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.ssooidc + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/ssooidc/src/main/resources/codegen-resources/paginators-1.json b/services/ssooidc/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..5677bd8e4a2d --- /dev/null +++ b/services/ssooidc/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,4 @@ +{ + "pagination": { + } +} diff --git a/services/ssooidc/src/main/resources/codegen-resources/service-2.json b/services/ssooidc/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..79b2efe1304d --- /dev/null +++ b/services/ssooidc/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,392 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-06-10", + "endpointPrefix":"oidc", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"SSO OIDC", + "serviceFullName":"AWS SSO OIDC", + "serviceId":"SSO OIDC", + "signatureVersion":"v4", + "signingName":"awsssooidc", + "uid":"sso-oidc-2019-06-10" + }, + "operations":{ + "CreateToken":{ + "name":"CreateToken", + "http":{ + "method":"POST", + "requestUri":"/token" + }, + "input":{"shape":"CreateTokenRequest"}, + "output":{"shape":"CreateTokenResponse"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"InvalidClientException"}, + {"shape":"InvalidGrantException"}, + {"shape":"UnauthorizedClientException"}, + {"shape":"UnsupportedGrantTypeException"}, + {"shape":"InvalidScopeException"}, + {"shape":"AuthorizationPendingException"}, + {"shape":"SlowDownException"}, + {"shape":"AccessDeniedException"}, + {"shape":"ExpiredTokenException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Creates and returns an access token for the authorized client. The access token issued will be used to fetch short-term credentials for the assigned roles in the AWS account.

    ", + "authtype":"none" + }, + "RegisterClient":{ + "name":"RegisterClient", + "http":{ + "method":"POST", + "requestUri":"/client/register" + }, + "input":{"shape":"RegisterClientRequest"}, + "output":{"shape":"RegisterClientResponse"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"InvalidScopeException"}, + {"shape":"InvalidClientMetadataException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Registers a client with AWS SSO. This allows clients to initiate device authorization. The output should be persisted for reuse through many authentication requests.

    ", + "authtype":"none" + }, + "StartDeviceAuthorization":{ + "name":"StartDeviceAuthorization", + "http":{ + "method":"POST", + "requestUri":"/device_authorization" + }, + "input":{"shape":"StartDeviceAuthorizationRequest"}, + "output":{"shape":"StartDeviceAuthorizationResponse"}, + "errors":[ + {"shape":"InvalidRequestException"}, + {"shape":"InvalidClientException"}, + {"shape":"UnauthorizedClientException"}, + {"shape":"SlowDownException"}, + {"shape":"InternalServerException"} + ], + "documentation":"

    Initiates device authorization by requesting a pair of verification codes from the authorization service.

    ", + "authtype":"none" + } + }, + "shapes":{ + "AccessDeniedException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    You do not have sufficient access to perform this action.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "AccessToken":{"type":"string"}, + "AuthCode":{"type":"string"}, + "AuthorizationPendingException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that a request to authorize a client with an access user session token is pending.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "ClientId":{"type":"string"}, + "ClientName":{"type":"string"}, + "ClientSecret":{"type":"string"}, + "ClientType":{"type":"string"}, + "CreateTokenRequest":{ + "type":"structure", + "required":[ + "clientId", + "clientSecret", + "grantType", + "deviceCode" + ], + "members":{ + "clientId":{ + "shape":"ClientId", + "documentation":"

    The unique identifier string for each client. This value should come from the persisted result of the RegisterClient API.

    " + }, + "clientSecret":{ + "shape":"ClientSecret", + "documentation":"

    A secret string generated for the client. This value should come from the persisted result of the RegisterClient API.

    " + }, + "grantType":{ + "shape":"GrantType", + "documentation":"

    Supports grant types for authorization code, refresh token, and device code request.

    " + }, + "deviceCode":{ + "shape":"DeviceCode", + "documentation":"

    Used only when calling this API for the device code grant type. This short-term code is used to identify this authentication attempt. This should come from an in-memory reference to the result of the StartDeviceAuthorization API.

    " + }, + "code":{ + "shape":"AuthCode", + "documentation":"

    The authorization code received from the authorization service. This parameter is required to perform an authorization grant request to get access to a token.

    " + }, + "refreshToken":{ + "shape":"RefreshToken", + "documentation":"

    The token used to obtain an access token in the event that the access token is invalid or expired. This token is not issued by the service.

    " + }, + "scope":{ + "shape":"Scopes", + "documentation":"

    The list of scopes that is defined by the client. Upon authorization, this list is used to restrict permissions when granting an access token.

    " + }, + "redirectUri":{ + "shape":"URI", + "documentation":"

    The location of the application that will receive the authorization code. Users authorize the service to send the request to this location.

    " + } + } + }, + "CreateTokenResponse":{ + "type":"structure", + "members":{ + "accessToken":{ + "shape":"AccessToken", + "documentation":"

    An opaque token to access AWS SSO resources assigned to a user.

    " + }, + "tokenType":{ + "shape":"TokenType", + "documentation":"

    Used to notify the client that the returned token is an access token. The supported type is BearerToken.

    " + }, + "expiresIn":{ + "shape":"ExpirationInSeconds", + "documentation":"

    Indicates the time in seconds when an access token will expire.

    " + }, + "refreshToken":{ + "shape":"RefreshToken", + "documentation":"

    A token that, if present, can be used to refresh a previously issued access token that might have expired.

    " + }, + "idToken":{ + "shape":"IdToken", + "documentation":"

    The identifier of the user that associated with the access token, if present.

    " + } + } + }, + "DeviceCode":{"type":"string"}, + "Error":{"type":"string"}, + "ErrorDescription":{"type":"string"}, + "ExpirationInSeconds":{"type":"integer"}, + "ExpiredTokenException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the token issued by the service is expired and is no longer valid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "GrantType":{"type":"string"}, + "IdToken":{"type":"string"}, + "InternalServerException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that an error from the service occurred while trying to process a request.

    ", + "error":{"httpStatusCode":500}, + "exception":true, + "fault":true + }, + "IntervalInSeconds":{"type":"integer"}, + "InvalidClientException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the clientId or clientSecret in the request is invalid. For example, this can occur when a client sends an incorrect clientId or an expired clientSecret.

    ", + "error":{"httpStatusCode":401}, + "exception":true + }, + "InvalidClientMetadataException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the client information sent in the request during registration is invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidGrantException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that a request contains an invalid grant. This can occur if a client makes a CreateToken request with an invalid grant type.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidRequestException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that something is wrong with the input to the request. For example, a required parameter might be missing or out of range.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "InvalidScopeException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the scope provided in the request is invalid.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "LongTimeStampType":{"type":"long"}, + "RefreshToken":{"type":"string"}, + "RegisterClientRequest":{ + "type":"structure", + "required":[ + "clientName", + "clientType" + ], + "members":{ + "clientName":{ + "shape":"ClientName", + "documentation":"

    The friendly name of the client.

    " + }, + "clientType":{ + "shape":"ClientType", + "documentation":"

    The type of client. The service supports only public as a client type. Anything other than public will be rejected by the service.

    " + }, + "scopes":{ + "shape":"Scopes", + "documentation":"

    The list of scopes that are defined by the client. Upon authorization, this list is used to restrict permissions when granting an access token.

    " + } + } + }, + "RegisterClientResponse":{ + "type":"structure", + "members":{ + "clientId":{ + "shape":"ClientId", + "documentation":"

    The unique identifier string for each client. This client uses this identifier to get authenticated by the service in subsequent calls.

    " + }, + "clientSecret":{ + "shape":"ClientSecret", + "documentation":"

    A secret string generated for the client. The client will use this string to get authenticated by the service in subsequent calls.

    " + }, + "clientIdIssuedAt":{ + "shape":"LongTimeStampType", + "documentation":"

    Indicates the time at which the clientId and clientSecret were issued.

    " + }, + "clientSecretExpiresAt":{ + "shape":"LongTimeStampType", + "documentation":"

    Indicates the time at which the clientId and clientSecret will become invalid.

    " + }, + "authorizationEndpoint":{ + "shape":"URI", + "documentation":"

    The endpoint where the client can request authorization.

    " + }, + "tokenEndpoint":{ + "shape":"URI", + "documentation":"

    The endpoint where the client can get an access token.

    " + } + } + }, + "Scope":{"type":"string"}, + "Scopes":{ + "type":"list", + "member":{"shape":"Scope"} + }, + "SlowDownException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the client is making the request too frequently and is more than the service can handle.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "StartDeviceAuthorizationRequest":{ + "type":"structure", + "required":[ + "clientId", + "clientSecret", + "startUrl" + ], + "members":{ + "clientId":{ + "shape":"ClientId", + "documentation":"

    The unique identifier string for the client that is registered with AWS SSO. This value should come from the persisted result of the RegisterClient API operation.

    " + }, + "clientSecret":{ + "shape":"ClientSecret", + "documentation":"

    A secret string that is generated for the client. This value should come from the persisted result of the RegisterClient API operation.

    " + }, + "startUrl":{ + "shape":"URI", + "documentation":"

    The URL for the AWS SSO user portal. For more information, see Using the User Portal in the AWS Single Sign-On User Guide.

    " + } + } + }, + "StartDeviceAuthorizationResponse":{ + "type":"structure", + "members":{ + "deviceCode":{ + "shape":"DeviceCode", + "documentation":"

    The short-lived code that is used by the device when polling for a session token.

    " + }, + "userCode":{ + "shape":"UserCode", + "documentation":"

    A one-time user verification code. This is needed to authorize an in-use device.

    " + }, + "verificationUri":{ + "shape":"URI", + "documentation":"

    The URI of the verification page that takes the userCode to authorize the device.

    " + }, + "verificationUriComplete":{ + "shape":"URI", + "documentation":"

    An alternate URL that the client can use to automatically launch a browser. This process skips the manual step in which the user visits the verification page and enters their code.

    " + }, + "expiresIn":{ + "shape":"ExpirationInSeconds", + "documentation":"

    Indicates the number of seconds in which the verification code will become invalid.

    " + }, + "interval":{ + "shape":"IntervalInSeconds", + "documentation":"

    Indicates the number of seconds the client must wait between attempts when polling for a session.

    " + } + } + }, + "TokenType":{"type":"string"}, + "URI":{"type":"string"}, + "UnauthorizedClientException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the client is not currently authorized to make the request. This can happen when a clientId is not issued for a public client.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "UnsupportedGrantTypeException":{ + "type":"structure", + "members":{ + "error":{"shape":"Error"}, + "error_description":{"shape":"ErrorDescription"} + }, + "documentation":"

    Indicates that the grant type in the request is not supported by the service.

    ", + "error":{"httpStatusCode":400}, + "exception":true + }, + "UserCode":{"type":"string"} + }, + "documentation":"

    AWS Single Sign-On (SSO) OpenID Connect (OIDC) is a web service that enables a client (such as AWS CLI or a native application) to register with AWS SSO. The service also enables the client to fetch the user’s access token upon successful authentication and authorization with AWS SSO. This service conforms with the OAuth 2.0 based implementation of the device authorization grant standard (https://tools.ietf.org/html/rfc8628).

    For general information about AWS SSO, see What is AWS Single Sign-On? in the AWS SSO User Guide.

    This API reference guide describes the AWS SSO OIDC operations that you can call programatically and includes detailed information on data types and errors.

    AWS provides SDKs that consist of libraries and sample code for various programming languages and platforms such as Java, Ruby, .Net, iOS, and Android. The SDKs provide a convenient way to create programmatic access to AWS SSO and other AWS services. For more information about the AWS SDKs, including how to download and install them, see Tools for Amazon Web Services.

    " +} diff --git a/services/storagegateway/build.properties b/services/storagegateway/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/storagegateway/build.properties +++ b/services/storagegateway/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/storagegateway/pom.xml b/services/storagegateway/pom.xml index fb2d6173673d..97282925f0d6 100644 --- a/services/storagegateway/pom.xml +++ b/services/storagegateway/pom.xml @@ -1,6 +1,6 @@ + + + 4.0.0 + + software.amazon.awssdk + services + 2.11.8-SNAPSHOT + + wafv2 + AWS Java SDK :: Services :: WAFV2 + The AWS Java SDK for WAFV2 module holds the client classes that are used for + communicating with WAFV2. + + https://aws.amazon.com/sdkforjava + + + + org.apache.maven.plugins + maven-jar-plugin + + + + software.amazon.awssdk.services.wafv2 + + + + + + + + + + software.amazon.awssdk + protocol-core + ${awsjavasdk.version} + + + software.amazon.awssdk + aws-json-protocol + ${awsjavasdk.version} + + + diff --git a/services/wafv2/src/main/resources/codegen-resources/paginators-1.json b/services/wafv2/src/main/resources/codegen-resources/paginators-1.json new file mode 100644 index 000000000000..5677bd8e4a2d --- /dev/null +++ b/services/wafv2/src/main/resources/codegen-resources/paginators-1.json @@ -0,0 +1,4 @@ +{ + "pagination": { + } +} diff --git a/services/wafv2/src/main/resources/codegen-resources/service-2.json b/services/wafv2/src/main/resources/codegen-resources/service-2.json new file mode 100644 index 000000000000..6b2dbf9d250a --- /dev/null +++ b/services/wafv2/src/main/resources/codegen-resources/service-2.json @@ -0,0 +1,3728 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2019-07-29", + "endpointPrefix":"wafv2", + "jsonVersion":"1.1", + "protocol":"json", + "serviceAbbreviation":"WAFV2", + "serviceFullName":"AWS WAFV2", + "serviceId":"WAFV2", + "signatureVersion":"v4", + "targetPrefix":"AWSWAF_20190729", + "uid":"wafv2-2019-07-29" + }, + "operations":{ + "AssociateWebACL":{ + "name":"AssociateWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"AssociateWebACLRequest"}, + "output":{"shape":"AssociateWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Associates a Web ACL with a regional application resource, to protect the resource. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    For AWS CloudFront, don't use this call. Instead, use your CloudFront distribution configuration. To associate a Web ACL, in the CloudFront call UpdateDistribution, set the web ACL ID to the Amazon Resource Name (ARN) of the Web ACL. For information, see UpdateDistribution.

    " + }, + "CheckCapacity":{ + "name":"CheckCapacity", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CheckCapacityRequest"}, + "output":{"shape":"CheckCapacityResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFInvalidResourceException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFSubscriptionNotFoundException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Returns the web ACL capacity unit (WCU) requirements for a specified scope and set of rules. You can use this to check the capacity requirements for the rules you want to use in a RuleGroup or WebACL.

    AWS WAF uses WCUs to calculate and control the operating resources that are used to run your rules, rule groups, and web ACLs. AWS WAF calculates capacity differently for each rule type, to reflect the relative cost of each rule. Simple rules that cost little to run use fewer WCUs than more complex rules that use more processing power. Rule group capacity is fixed at creation, which helps users plan their web ACL WCU usage when they use a rule group. The WCU limit for web ACLs is 1,500.

    " + }, + "CreateIPSet":{ + "name":"CreateIPSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateIPSetRequest"}, + "output":{"shape":"CreateIPSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Creates an IPSet, which you use to identify web requests that originate from specific IP addresses or ranges of IP addresses. For example, if you're receiving a lot of requests from a ranges of IP addresses, you can configure AWS WAF to block them using an IPSet that lists those IP addresses.

    " + }, + "CreateRegexPatternSet":{ + "name":"CreateRegexPatternSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateRegexPatternSetRequest"}, + "output":{"shape":"CreateRegexPatternSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Creates a RegexPatternSet, which you reference in a RegexPatternSetReferenceStatement, to have AWS WAF inspect a web request component for the specified patterns.

    " + }, + "CreateRuleGroup":{ + "name":"CreateRuleGroup", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateRuleGroupRequest"}, + "output":{"shape":"CreateRuleGroupResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFSubscriptionNotFoundException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Creates a RuleGroup per the specifications provided.

    A rule group defines a collection of rules to inspect and control web requests that you can use in a WebACL. When you create a rule group, you define an immutable capacity limit. If you update a rule group, you must stay within the capacity. This allows others to reuse the rule group with confidence in its capacity requirements.

    " + }, + "CreateWebACL":{ + "name":"CreateWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"CreateWebACLRequest"}, + "output":{"shape":"CreateWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFInvalidResourceException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFSubscriptionNotFoundException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Creates a WebACL per the specifications provided.

    A Web ACL defines a collection of rules to use to inspect and control web requests. Each rule has an action defined (allow, block, or count) for requests that match the statement of the rule. In the Web ACL, you assign a default action to take (allow, block) for any request that does not match any of the rules. The rules in a Web ACL can be a combination of the types Rule, RuleGroup, and managed rule group. You can associate a Web ACL with one or more AWS resources to protect. The resources can be Amazon CloudFront, an Amazon API Gateway API, or an Application Load Balancer.

    " + }, + "DeleteFirewallManagerRuleGroups":{ + "name":"DeleteFirewallManagerRuleGroups", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteFirewallManagerRuleGroupsRequest"}, + "output":{"shape":"DeleteFirewallManagerRuleGroupsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    Deletes all rule groups that are managed by AWS Firewall Manager for the specified web ACL.

    You can only use this if ManagedByFirewallManager is false in the specified WebACL.

    " + }, + "DeleteIPSet":{ + "name":"DeleteIPSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteIPSetRequest"}, + "output":{"shape":"DeleteIPSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFAssociatedItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Deletes the specified IPSet.

    " + }, + "DeleteLoggingConfiguration":{ + "name":"DeleteLoggingConfiguration", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteLoggingConfigurationRequest"}, + "output":{"shape":"DeleteLoggingConfigurationResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Deletes the LoggingConfiguration from the specified web ACL.

    " + }, + "DeletePermissionPolicy":{ + "name":"DeletePermissionPolicy", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeletePermissionPolicyRequest"}, + "output":{"shape":"DeletePermissionPolicyResponse"}, + "errors":[ + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"} + ], + "documentation":"

    Permanently deletes an IAM policy from the specified rule group.

    You must be the owner of the rule group to perform this operation.

    " + }, + "DeleteRegexPatternSet":{ + "name":"DeleteRegexPatternSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteRegexPatternSetRequest"}, + "output":{"shape":"DeleteRegexPatternSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFAssociatedItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Deletes the specified RegexPatternSet.

    " + }, + "DeleteRuleGroup":{ + "name":"DeleteRuleGroup", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteRuleGroupRequest"}, + "output":{"shape":"DeleteRuleGroupResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFAssociatedItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Deletes the specified RuleGroup.

    " + }, + "DeleteWebACL":{ + "name":"DeleteWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DeleteWebACLRequest"}, + "output":{"shape":"DeleteWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFAssociatedItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Deletes the specified WebACL.

    You can only use this if ManagedByFirewallManager is false in the specified WebACL.

    " + }, + "DescribeManagedRuleGroup":{ + "name":"DescribeManagedRuleGroup", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DescribeManagedRuleGroupRequest"}, + "output":{"shape":"DescribeManagedRuleGroupResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidResourceException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Provides high-level information for a managed rule group, including descriptions of the rules.

    " + }, + "DisassociateWebACL":{ + "name":"DisassociateWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"DisassociateWebACLRequest"}, + "output":{"shape":"DisassociateWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Disassociates a Web ACL from a regional application resource. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    For AWS CloudFront, don't use this call. Instead, use your CloudFront distribution configuration. To disassociate a Web ACL, provide an empty web ACL ID in the CloudFront call UpdateDistribution. For information, see UpdateDistribution.

    " + }, + "GetIPSet":{ + "name":"GetIPSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetIPSetRequest"}, + "output":{"shape":"GetIPSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the specified IPSet.

    " + }, + "GetLoggingConfiguration":{ + "name":"GetLoggingConfiguration", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetLoggingConfigurationRequest"}, + "output":{"shape":"GetLoggingConfigurationResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Returns the LoggingConfiguration for the specified web ACL.

    " + }, + "GetPermissionPolicy":{ + "name":"GetPermissionPolicy", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetPermissionPolicyRequest"}, + "output":{"shape":"GetPermissionPolicyResponse"}, + "errors":[ + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"} + ], + "documentation":"

    Returns the IAM policy that is attached to the specified rule group.

    You must be the owner of the rule group to perform this operation.

    " + }, + "GetRateBasedStatementManagedKeys":{ + "name":"GetRateBasedStatementManagedKeys", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetRateBasedStatementManagedKeysRequest"}, + "output":{"shape":"GetRateBasedStatementManagedKeysResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the keys that are currently blocked by a rate-based rule. The maximum number of managed keys that can be blocked for a single rate-based rule is 10,000. If more than 10,000 addresses exceed the rate limit, those with the highest rates are blocked.

    " + }, + "GetRegexPatternSet":{ + "name":"GetRegexPatternSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetRegexPatternSetRequest"}, + "output":{"shape":"GetRegexPatternSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the specified RegexPatternSet.

    " + }, + "GetRuleGroup":{ + "name":"GetRuleGroup", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetRuleGroupRequest"}, + "output":{"shape":"GetRuleGroupResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the specified RuleGroup.

    " + }, + "GetSampledRequests":{ + "name":"GetSampledRequests", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetSampledRequestsRequest"}, + "output":{"shape":"GetSampledRequestsResponse"}, + "errors":[ + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Gets detailed information about a specified number of requests--a sample--that AWS WAF randomly selects from among the first 5,000 requests that your AWS resource received during a time range that you choose. You can specify a sample size of up to 500 requests, and you can specify any time range in the previous three hours.

    GetSampledRequests returns a time range, which is usually the time range that you specified. However, if your resource (such as a CloudFront distribution) received 5,000 requests before the specified time range elapsed, GetSampledRequests returns an updated time range. This new time range indicates the actual period during which AWS WAF selected the requests in the sample.

    " + }, + "GetWebACL":{ + "name":"GetWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetWebACLRequest"}, + "output":{"shape":"GetWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the specified WebACL.

    " + }, + "GetWebACLForResource":{ + "name":"GetWebACLForResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"GetWebACLForResourceRequest"}, + "output":{"shape":"GetWebACLForResourceResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the WebACL for the specified resource.

    " + }, + "ListAvailableManagedRuleGroups":{ + "name":"ListAvailableManagedRuleGroups", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListAvailableManagedRuleGroupsRequest"}, + "output":{"shape":"ListAvailableManagedRuleGroupsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of managed rule groups that are available for you to use. This list includes all AWS Managed Rules rule groups and the AWS Marketplace managed rule groups that you're subscribed to.

    " + }, + "ListIPSets":{ + "name":"ListIPSets", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListIPSetsRequest"}, + "output":{"shape":"ListIPSetsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of IPSetSummary objects for the IP sets that you manage.

    " + }, + "ListLoggingConfigurations":{ + "name":"ListLoggingConfigurations", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListLoggingConfigurationsRequest"}, + "output":{"shape":"ListLoggingConfigurationsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of your LoggingConfiguration objects.

    " + }, + "ListRegexPatternSets":{ + "name":"ListRegexPatternSets", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListRegexPatternSetsRequest"}, + "output":{"shape":"ListRegexPatternSetsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of RegexPatternSetSummary objects for the regex pattern sets that you manage.

    " + }, + "ListResourcesForWebACL":{ + "name":"ListResourcesForWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListResourcesForWebACLRequest"}, + "output":{"shape":"ListResourcesForWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of the Amazon Resource Names (ARNs) for the regional resources that are associated with the specified web ACL. If you want the list of AWS CloudFront resources, use the AWS CloudFront call ListDistributionsByWebACLId.

    " + }, + "ListRuleGroups":{ + "name":"ListRuleGroups", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListRuleGroupsRequest"}, + "output":{"shape":"ListRuleGroupsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of RuleGroupSummary objects for the rule groups that you manage.

    " + }, + "ListTagsForResource":{ + "name":"ListTagsForResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListTagsForResourceRequest"}, + "output":{"shape":"ListTagsForResourceResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves the TagInfoForResource for the specified resource.

    " + }, + "ListWebACLs":{ + "name":"ListWebACLs", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"ListWebACLsRequest"}, + "output":{"shape":"ListWebACLsResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Retrieves an array of WebACLSummary objects for the web ACLs that you manage.

    " + }, + "PutLoggingConfiguration":{ + "name":"PutLoggingConfiguration", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutLoggingConfigurationRequest"}, + "output":{"shape":"PutLoggingConfigurationResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFServiceLinkedRoleErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Enables the specified LoggingConfiguration, to start logging from a web ACL, according to the configuration provided.

    You can access information about all traffic that AWS WAF inspects using the following steps:

    1. Create an Amazon Kinesis Data Firehose.

      Create the data firehose with a PUT source and in the Region that you are operating. If you are capturing logs for Amazon CloudFront, always create the firehose in US East (N. Virginia).

      Do not create the data firehose using a Kinesis stream as your source.

    2. Associate that firehose to your web ACL using a PutLoggingConfiguration request.

    When you successfully enable logging using a PutLoggingConfiguration request, AWS WAF will create a service linked role with the necessary permissions to write logs to the Amazon Kinesis Data Firehose. For more information, see Logging Web ACL Traffic Information in the AWS WAF Developer Guide.

    " + }, + "PutPermissionPolicy":{ + "name":"PutPermissionPolicy", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"PutPermissionPolicyRequest"}, + "output":{"shape":"PutPermissionPolicyResponse"}, + "errors":[ + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFInvalidPermissionPolicyException"} + ], + "documentation":"

    Attaches an IAM policy to the specified resource. Use this to share a rule group across accounts.

    You must be the owner of the rule group to perform this operation.

    This action is subject to the following restrictions:

    • You can attach only one policy with each PutPermissionPolicy request.

    • The ARN in the request must be a valid WAF RuleGroup ARN and the rule group must exist in the same region.

    • The user making the request must be the owner of the rule group.

    " + }, + "TagResource":{ + "name":"TagResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"TagResourceRequest"}, + "output":{"shape":"TagResourceResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Associates tags with the specified AWS resource. Tags are key:value pairs that you can associate with AWS resources. For example, the tag key might be \"customer\" and the tag value might be \"companyA.\" You can specify one or more tags to add to each container. You can add up to 50 tags to each AWS resource.

    " + }, + "UntagResource":{ + "name":"UntagResource", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UntagResourceRequest"}, + "output":{"shape":"UntagResourceResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFTagOperationException"}, + {"shape":"WAFTagOperationInternalErrorException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Disassociates tags from an AWS resource. Tags are key:value pairs that you can associate with AWS resources. For example, the tag key might be \"customer\" and the tag value might be \"companyA.\" You can specify one or more tags to add to each container. You can add up to 50 tags to each AWS resource.

    " + }, + "UpdateIPSet":{ + "name":"UpdateIPSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateIPSetRequest"}, + "output":{"shape":"UpdateIPSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Updates the specified IPSet.

    " + }, + "UpdateRegexPatternSet":{ + "name":"UpdateRegexPatternSet", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateRegexPatternSetRequest"}, + "output":{"shape":"UpdateRegexPatternSetResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Updates the specified RegexPatternSet.

    " + }, + "UpdateRuleGroup":{ + "name":"UpdateRuleGroup", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateRuleGroupRequest"}, + "output":{"shape":"UpdateRuleGroupResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFSubscriptionNotFoundException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Updates the specified RuleGroup.

    A rule group defines a collection of rules to inspect and control web requests that you can use in a WebACL. When you create a rule group, you define an immutable capacity limit. If you update a rule group, you must stay within the capacity. This allows others to reuse the rule group with confidence in its capacity requirements.

    " + }, + "UpdateWebACL":{ + "name":"UpdateWebACL", + "http":{ + "method":"POST", + "requestUri":"/" + }, + "input":{"shape":"UpdateWebACLRequest"}, + "output":{"shape":"UpdateWebACLResponse"}, + "errors":[ + {"shape":"WAFInternalErrorException"}, + {"shape":"WAFInvalidParameterException"}, + {"shape":"WAFNonexistentItemException"}, + {"shape":"WAFDuplicateItemException"}, + {"shape":"WAFOptimisticLockException"}, + {"shape":"WAFLimitsExceededException"}, + {"shape":"WAFInvalidResourceException"}, + {"shape":"WAFUnavailableEntityException"}, + {"shape":"WAFSubscriptionNotFoundException"}, + {"shape":"WAFInvalidOperationException"} + ], + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Updates the specified WebACL.

    A Web ACL defines a collection of rules to use to inspect and control web requests. Each rule has an action defined (allow, block, or count) for requests that match the statement of the rule. In the Web ACL, you assign a default action to take (allow, block) for any request that does not match any of the rules. The rules in a Web ACL can be a combination of the types Rule, RuleGroup, and managed rule group. You can associate a Web ACL with one or more AWS resources to protect. The resources can be Amazon CloudFront, an Amazon API Gateway API, or an Application Load Balancer.

    " + } + }, + "shapes":{ + "Action":{"type":"string"}, + "AllQueryArguments":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    All query arguments of a web request.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "AllowAction":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Specifies that AWS WAF should allow requests.

    This is used only in the context of other settings, for example to specify values for RuleAction and web ACL DefaultAction.

    " + }, + "AndStatement":{ + "type":"structure", + "required":["Statements"], + "members":{ + "Statements":{ + "shape":"Statements", + "documentation":"

    The statements to combine with AND logic. You can use any statements that can be nested.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A logical rule statement used to combine other rule statements with AND logic. You provide more than one Statement within the AndStatement.

    " + }, + "AssociateWebACLRequest":{ + "type":"structure", + "required":[ + "WebACLArn", + "ResourceArn" + ], + "members":{ + "WebACLArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the Web ACL that you want to associate with the resource.

    " + }, + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource to associate with the web ACL.

    The ARN must be in one of the following formats:

    • For an Application Load Balancer: arn:aws:elasticloadbalancing:region:account-id:loadbalancer/app/load-balancer-name/load-balancer-id

    • For an Amazon API Gateway stage: arn:aws:apigateway:region::/restapis/api-id/stages/stage-name

    " + } + } + }, + "AssociateWebACLResponse":{ + "type":"structure", + "members":{ + } + }, + "BlockAction":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Specifies that AWS WAF should block requests.

    This is used only in the context of other settings, for example to specify values for RuleAction and web ACL DefaultAction.

    " + }, + "Body":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The body of a web request. This immediately follows the request headers.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "Boolean":{"type":"boolean"}, + "ByteMatchStatement":{ + "type":"structure", + "required":[ + "SearchString", + "FieldToMatch", + "TextTransformations", + "PositionalConstraint" + ], + "members":{ + "SearchString":{ + "shape":"SearchString", + "documentation":"

    A string value that you want AWS WAF to search for. AWS WAF searches only in the part of web requests that you designate for inspection in FieldToMatch. The maximum length of the value is 50 bytes.

    Valid values depend on the component that you specify for inspection in FieldToMatch:

    • Method: The HTTP method that you want AWS WAF to search for. This indicates the type of operation specified in the request.

    • UriPath: The value that you want AWS WAF to search for in the URI path, for example, /images/daily-ad.jpg.

    If SearchString includes alphabetic characters A-Z and a-z, note that the value is case sensitive.

    If you're using the AWS WAF API

    Specify a base64-encoded version of the value. The maximum length of the value before you base64-encode it is 50 bytes.

    For example, suppose the value of Type is HEADER and the value of Data is User-Agent. If you want to search the User-Agent header for the value BadBot, you base64-encode BadBot using MIME base64-encoding and include the resulting value, QmFkQm90, in the value of SearchString.

    If you're using the AWS CLI or one of the AWS SDKs

    The value that you want AWS WAF to search for. The SDK automatically base64 encodes the value.

    " + }, + "FieldToMatch":{ + "shape":"FieldToMatch", + "documentation":"

    The part of a web request that you want AWS WAF to inspect. For more information, see FieldToMatch.

    " + }, + "TextTransformations":{ + "shape":"TextTransformations", + "documentation":"

    Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. If you specify one or more transformations in a rule statement, AWS WAF performs all transformations on the content of the request component identified by FieldToMatch, starting from the lowest priority setting, before inspecting the content for a match.

    " + }, + "PositionalConstraint":{ + "shape":"PositionalConstraint", + "documentation":"

    The area within the portion of a web request that you want AWS WAF to search for SearchString. Valid values include the following:

    CONTAINS

    The specified part of the web request must include the value of SearchString, but the location doesn't matter.

    CONTAINS_WORD

    The specified part of the web request must include the value of SearchString, and SearchString must contain only alphanumeric characters or underscore (A-Z, a-z, 0-9, or _). In addition, SearchString must be a word, which means that both of the following are true:

    • SearchString is at the beginning of the specified part of the web request or is preceded by a character other than an alphanumeric character or underscore (_). Examples include the value of a header and ;BadBot.

    • SearchString is at the end of the specified part of the web request or is followed by a character other than an alphanumeric character or underscore (_), for example, BadBot; and -BadBot;.

    EXACTLY

    The value of the specified part of the web request must exactly match the value of SearchString.

    STARTS_WITH

    The value of SearchString must appear at the beginning of the specified part of the web request.

    ENDS_WITH

    The value of SearchString must appear at the end of the specified part of the web request.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement that defines a string match search for AWS WAF to apply to web requests. The byte match statement provides the bytes to search for, the location in requests that you want AWS WAF to search, and other settings. The bytes to search for are typically a string that corresponds with ASCII characters. In the AWS WAF console and the developer guide, this is refered to as a string match statement.

    " + }, + "CapacityUnit":{ + "type":"long", + "min":1 + }, + "CheckCapacityRequest":{ + "type":"structure", + "required":[ + "Scope", + "Rules" + ], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    An array of Rule that you're configuring to use in a rule group or web ACL.

    " + } + } + }, + "CheckCapacityResponse":{ + "type":"structure", + "members":{ + "Capacity":{ + "shape":"ConsumedCapacity", + "documentation":"

    The capacity required by the rules and scope.

    " + } + } + }, + "ComparisonOperator":{ + "type":"string", + "enum":[ + "EQ", + "NE", + "LE", + "LT", + "GE", + "GT" + ] + }, + "ConsumedCapacity":{ + "type":"long", + "min":0 + }, + "CountAction":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Specifies that AWS WAF should count requests.

    This is used only in the context of other settings, for example to specify values for RuleAction and web ACL DefaultAction.

    " + }, + "Country":{"type":"string"}, + "CountryCode":{ + "type":"string", + "enum":[ + "AF", + "AX", + "AL", + "DZ", + "AS", + "AD", + "AO", + "AI", + "AQ", + "AG", + "AR", + "AM", + "AW", + "AU", + "AT", + "AZ", + "BS", + "BH", + "BD", + "BB", + "BY", + "BE", + "BZ", + "BJ", + "BM", + "BT", + "BO", + "BQ", + "BA", + "BW", + "BV", + "BR", + "IO", + "BN", + "BG", + "BF", + "BI", + "KH", + "CM", + "CA", + "CV", + "KY", + "CF", + "TD", + "CL", + "CN", + "CX", + "CC", + "CO", + "KM", + "CG", + "CD", + "CK", + "CR", + "CI", + "HR", + "CU", + "CW", + "CY", + "CZ", + "DK", + "DJ", + "DM", + "DO", + "EC", + "EG", + "SV", + "GQ", + "ER", + "EE", + "ET", + "FK", + "FO", + "FJ", + "FI", + "FR", + "GF", + "PF", + "TF", + "GA", + "GM", + "GE", + "DE", + "GH", + "GI", + "GR", + "GL", + "GD", + "GP", + "GU", + "GT", + "GG", + "GN", + "GW", + "GY", + "HT", + "HM", + "VA", + "HN", + "HK", + "HU", + "IS", + "IN", + "ID", + "IR", + "IQ", + "IE", + "IM", + "IL", + "IT", + "JM", + "JP", + "JE", + "JO", + "KZ", + "KE", + "KI", + "KP", + "KR", + "KW", + "KG", + "LA", + "LV", + "LB", + "LS", + "LR", + "LY", + "LI", + "LT", + "LU", + "MO", + "MK", + "MG", + "MW", + "MY", + "MV", + "ML", + "MT", + "MH", + "MQ", + "MR", + "MU", + "YT", + "MX", + "FM", + "MD", + "MC", + "MN", + "ME", + "MS", + "MA", + "MZ", + "MM", + "NA", + "NR", + "NP", + "NL", + "NC", + "NZ", + "NI", + "NE", + "NG", + "NU", + "NF", + "MP", + "NO", + "OM", + "PK", + "PW", + "PS", + "PA", + "PG", + "PY", + "PE", + "PH", + "PN", + "PL", + "PT", + "PR", + "QA", + "RE", + "RO", + "RU", + "RW", + "BL", + "SH", + "KN", + "LC", + "MF", + "PM", + "VC", + "WS", + "SM", + "ST", + "SA", + "SN", + "RS", + "SC", + "SL", + "SG", + "SX", + "SK", + "SI", + "SB", + "SO", + "ZA", + "GS", + "SS", + "ES", + "LK", + "SD", + "SR", + "SJ", + "SZ", + "SE", + "CH", + "SY", + "TW", + "TJ", + "TZ", + "TH", + "TL", + "TG", + "TK", + "TO", + "TT", + "TN", + "TR", + "TM", + "TC", + "TV", + "UG", + "UA", + "AE", + "GB", + "US", + "UM", + "UY", + "UZ", + "VU", + "VE", + "VN", + "VG", + "VI", + "WF", + "EH", + "YE", + "ZM", + "ZW" + ] + }, + "CountryCodes":{ + "type":"list", + "member":{"shape":"CountryCode"}, + "min":1 + }, + "CreateIPSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "IPAddressVersion", + "Addresses" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the IP set. You cannot change the name of an IPSet after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the IP set that helps with identification. You cannot change the description of an IP set after you create it.

    " + }, + "IPAddressVersion":{ + "shape":"IPAddressVersion", + "documentation":"

    Specify IPV4 or IPV6.

    " + }, + "Addresses":{ + "shape":"IPAddresses", + "documentation":"

    Contains an array of strings that specify one or more IP addresses or blocks of IP addresses in Classless Inter-Domain Routing (CIDR) notation. AWS WAF supports all address ranges for IP versions IPv4 and IPv6.

    Examples:

    • To configure AWS WAF to allow, block, or count requests that originated from the IP address 192.0.2.44, specify 192.0.2.44/32.

    • To configure AWS WAF to allow, block, or count requests that originated from IP addresses from 192.0.2.0 to 192.0.2.255, specify 192.0.2.0/24.

    • To configure AWS WAF to allow, block, or count requests that originated from the IP address 1111:0000:0000:0000:0000:0000:0000:0111, specify 1111:0000:0000:0000:0000:0000:0000:0111/128.

    • To configure AWS WAF to allow, block, or count requests that originated from IP addresses 1111:0000:0000:0000:0000:0000:0000:0000 to 1111:0000:0000:0000:ffff:ffff:ffff:ffff, specify 1111:0000:0000:0000:0000:0000:0000:0000/64.

    For more information about CIDR notation, see the Wikipedia entry Classless Inter-Domain Routing.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of key:value pairs to associate with the resource.

    " + } + } + }, + "CreateIPSetResponse":{ + "type":"structure", + "members":{ + "Summary":{ + "shape":"IPSetSummary", + "documentation":"

    High-level information about an IPSet, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage an IPSet, and the ARN, that you provide to the IPSetReferenceStatement to use the address set in a Rule.

    " + } + } + }, + "CreateRegexPatternSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "RegularExpressionList" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the set. You cannot change the name after you create the set.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the set that helps with identification. You cannot change the description of a set after you create it.

    " + }, + "RegularExpressionList":{ + "shape":"RegularExpressionList", + "documentation":"

    Array of regular expression strings.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of key:value pairs to associate with the resource.

    " + } + } + }, + "CreateRegexPatternSetResponse":{ + "type":"structure", + "members":{ + "Summary":{ + "shape":"RegexPatternSetSummary", + "documentation":"

    High-level information about a RegexPatternSet, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage a RegexPatternSet, and the ARN, that you provide to the RegexPatternSetReferenceStatement to use the pattern set in a Rule.

    " + } + } + }, + "CreateRuleGroupRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Capacity", + "VisibilityConfig" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule group. You cannot change the name of a rule group after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Capacity":{ + "shape":"CapacityUnit", + "documentation":"

    The web ACL capacity units (WCUs) required for this rule group.

    When you create your own rule group, you define this, and you cannot change it after creation. When you add or modify the rules in a rule group, AWS WAF enforces this limit. You can check the capacity for a set of rules using CheckCapacity.

    AWS WAF uses WCUs to calculate and control the operating resources that are used to run your rules, rule groups, and web ACLs. AWS WAF calculates capacity differently for each rule type, to reflect the relative cost of each rule. Simple rules that cost little to run use fewer WCUs than more complex rules that use more processing power. Rule group capacity is fixed at creation, which helps users plan their web ACL WCU usage when they use a rule group. The WCU limit for web ACLs is 1,500.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the rule group that helps with identification. You cannot change the description of a rule group after you create it.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    The Rule statements used to identify the web requests that you want to allow, block, or count. Each rule includes one top-level statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of key:value pairs to associate with the resource.

    " + } + } + }, + "CreateRuleGroupResponse":{ + "type":"structure", + "members":{ + "Summary":{ + "shape":"RuleGroupSummary", + "documentation":"

    High-level information about a RuleGroup, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage a RuleGroup, and the ARN, that you provide to the RuleGroupReferenceStatement to use the rule group in a Rule.

    " + } + } + }, + "CreateWebACLRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "DefaultAction", + "VisibilityConfig" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "DefaultAction":{ + "shape":"DefaultAction", + "documentation":"

    The action to perform if none of the Rules contained in the WebACL match.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the Web ACL that helps with identification. You cannot change the description of a Web ACL after you create it.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    The Rule statements used to identify the web requests that you want to allow, block, or count. Each rule includes one top-level statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of key:value pairs to associate with the resource.

    " + } + } + }, + "CreateWebACLResponse":{ + "type":"structure", + "members":{ + "Summary":{ + "shape":"WebACLSummary", + "documentation":"

    High-level information about a WebACL, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage a WebACL, and the ARN, that you provide to operations like AssociateWebACL.

    " + } + } + }, + "DefaultAction":{ + "type":"structure", + "members":{ + "Block":{ + "shape":"BlockAction", + "documentation":"

    Specifies that AWS WAF should block requests by default.

    " + }, + "Allow":{ + "shape":"AllowAction", + "documentation":"

    Specifies that AWS WAF should allow requests by default.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    In a WebACL, this is the action that you want AWS WAF to perform when a web request doesn't match any of the rules in the WebACL. The default action must be a terminating action, so count is not allowed.

    " + }, + "DeleteFirewallManagerRuleGroupsRequest":{ + "type":"structure", + "required":[ + "WebACLArn", + "WebACLLockToken" + ], + "members":{ + "WebACLArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the web ACL.

    " + }, + "WebACLLockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "DeleteFirewallManagerRuleGroupsResponse":{ + "type":"structure", + "members":{ + "NextWebACLLockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "DeleteIPSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the IP set. You cannot change the name of an IPSet after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "DeleteIPSetResponse":{ + "type":"structure", + "members":{ + } + }, + "DeleteLoggingConfigurationRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the web ACL from which you want to delete the LoggingConfiguration.

    " + } + } + }, + "DeleteLoggingConfigurationResponse":{ + "type":"structure", + "members":{ + } + }, + "DeletePermissionPolicyRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the rule group from which you want to delete the policy.

    You must be the owner of the rule group to perform this operation.

    " + } + } + }, + "DeletePermissionPolicyResponse":{ + "type":"structure", + "members":{ + } + }, + "DeleteRegexPatternSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the set. You cannot change the name after you create the set.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "DeleteRegexPatternSetResponse":{ + "type":"structure", + "members":{ + } + }, + "DeleteRuleGroupRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule group. You cannot change the name of a rule group after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the rule group. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "DeleteRuleGroupResponse":{ + "type":"structure", + "members":{ + } + }, + "DeleteWebACLRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    The unique identifier for the Web ACL. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "DeleteWebACLResponse":{ + "type":"structure", + "members":{ + } + }, + "DescribeManagedRuleGroupRequest":{ + "type":"structure", + "required":[ + "VendorName", + "Name", + "Scope" + ], + "members":{ + "VendorName":{ + "shape":"VendorName", + "documentation":"

    The name of the managed rule group vendor. You use this, along with the rule group name, to identify the rule group.

    " + }, + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the managed rule group. You use this, along with the vendor name, to identify the rule group.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + } + } + }, + "DescribeManagedRuleGroupResponse":{ + "type":"structure", + "members":{ + "Capacity":{ + "shape":"CapacityUnit", + "documentation":"

    The web ACL capacity units (WCUs) required for this rule group. AWS WAF uses web ACL capacity units (WCU) to calculate and control the operating resources that are used to run your rules, rule groups, and web ACLs. AWS WAF calculates capacity differently for each rule type, to reflect each rule's relative cost. Rule group capacity is fixed at creation, so users can plan their web ACL WCU usage when they use a rule group. The WCU limit for web ACLs is 1,500.

    " + }, + "Rules":{ + "shape":"RuleSummaries", + "documentation":"

    " + } + } + }, + "DisassociateWebACLRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource to disassociate from the web ACL.

    The ARN must be in one of the following formats:

    • For an Application Load Balancer: arn:aws:elasticloadbalancing:region:account-id:loadbalancer/app/load-balancer-name/load-balancer-id

    • For an Amazon API Gateway stage: arn:aws:apigateway:region::/restapis/api-id/stages/stage-name

    " + } + } + }, + "DisassociateWebACLResponse":{ + "type":"structure", + "members":{ + } + }, + "EntityDescription":{ + "type":"string", + "max":256, + "min":1, + "pattern":"^[\\w+=:#@/\\-,\\.][\\w+=:#@/\\-,\\.\\s]+[\\w+=:#@/\\-,\\.]$" + }, + "EntityId":{ + "type":"string", + "max":36, + "min":1, + "pattern":"^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$" + }, + "EntityName":{ + "type":"string", + "max":128, + "min":1, + "pattern":"^[\\w\\-]+$" + }, + "ErrorMessage":{"type":"string"}, + "ErrorReason":{"type":"string"}, + "ExcludedRule":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule to exclude.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Specifies a single rule to exclude from the rule group. Excluding a rule overrides its action setting for the rule group in the web ACL, setting it to COUNT. This effectively excludes the rule from acting on web requests.

    " + }, + "ExcludedRules":{ + "type":"list", + "member":{"shape":"ExcludedRule"} + }, + "FieldToMatch":{ + "type":"structure", + "members":{ + "SingleHeader":{ + "shape":"SingleHeader", + "documentation":"

    Inspect a single header. Provide the name of the header to inspect, for example, User-Agent or Referer. This setting isn't case sensitive.

    " + }, + "SingleQueryArgument":{ + "shape":"SingleQueryArgument", + "documentation":"

    Inspect a single query argument. Provide the name of the query argument to inspect, such as UserName or SalesRegion. The name can be up to 30 characters long and isn't case sensitive.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "AllQueryArguments":{ + "shape":"AllQueryArguments", + "documentation":"

    Inspect all query arguments.

    " + }, + "UriPath":{ + "shape":"UriPath", + "documentation":"

    Inspect the request URI path. This is the part of a web request that identifies a resource, for example, /images/daily-ad.jpg.

    " + }, + "QueryString":{ + "shape":"QueryString", + "documentation":"

    Inspect the query string. This is the part of a URL that appears after a ? character, if any.

    " + }, + "Body":{ + "shape":"Body", + "documentation":"

    Inspect the request body, which immediately follows the request headers. This is the part of a request that contains any additional data that you want to send to your web server as the HTTP request body, such as data from a form.

    Note that only the first 8 KB (8192 bytes) of the request body are forwarded to AWS WAF for inspection by the underlying host service. If you don't need to inspect more than 8 KB, you can guarantee that you don't allow additional bytes in by combining a statement that inspects the body of the web request, such as ByteMatchStatement or RegexPatternSetReferenceStatement, with a SizeConstraintStatement that enforces an 8 KB size limit on the body of the request. AWS WAF doesn't support inspecting the entire contents of web requests whose bodies exceed the 8 KB limit.

    " + }, + "Method":{ + "shape":"Method", + "documentation":"

    Inspect the HTTP method. The method indicates the type of operation that the request is asking the origin to perform.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The part of a web request that you want AWS WAF to inspect. Include the single FieldToMatch type that you want to inspect, with additional specifications as needed, according to the type. You specify a single request component in FieldToMatch for each rule statement that requires it. To inspect more than one component of a web request, create a separate rule statement for each component.

    " + }, + "FieldToMatchData":{ + "type":"string", + "max":64, + "min":1, + "pattern":".*\\S.*" + }, + "FirewallManagerRuleGroup":{ + "type":"structure", + "required":[ + "Name", + "Priority", + "FirewallManagerStatement", + "OverrideAction", + "VisibilityConfig" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule group. You cannot change the name of a rule group after you create it.

    " + }, + "Priority":{ + "shape":"RulePriority", + "documentation":"

    If you define more than one rule group in the first or last Firewall Manager rule groups, AWS WAF evaluates each request against the rule groups in order, starting from the lowest priority setting. The priorities don't need to be consecutive, but they must all be different.

    " + }, + "FirewallManagerStatement":{ + "shape":"FirewallManagerStatement", + "documentation":"

    The processing guidance for an AWS Firewall Manager rule. This is like a regular rule Statement, but it can only contain a rule group reference.

    " + }, + "OverrideAction":{"shape":"OverrideAction"}, + "VisibilityConfig":{"shape":"VisibilityConfig"} + }, + "documentation":"

    A rule group that's defined for an AWS Firewall Manager WAF policy.

    " + }, + "FirewallManagerRuleGroups":{ + "type":"list", + "member":{"shape":"FirewallManagerRuleGroup"} + }, + "FirewallManagerStatement":{ + "type":"structure", + "members":{ + "ManagedRuleGroupStatement":{"shape":"ManagedRuleGroupStatement"}, + "RuleGroupReferenceStatement":{"shape":"RuleGroupReferenceStatement"} + }, + "documentation":"

    The processing guidance for an AWS Firewall Manager rule. This is like a regular rule Statement, but it can only contain a rule group reference.

    " + }, + "GeoMatchStatement":{ + "type":"structure", + "members":{ + "CountryCodes":{ + "shape":"CountryCodes", + "documentation":"

    An array of two-character country codes, for example, [ \"US\", \"CN\" ], from the alpha-2 country ISO codes of the ISO 3166 international standard.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement used to identify web requests based on country of origin.

    " + }, + "GetIPSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the IP set. You cannot change the name of an IPSet after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + } + } + }, + "GetIPSetResponse":{ + "type":"structure", + "members":{ + "IPSet":{ + "shape":"IPSet", + "documentation":"

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "GetLoggingConfigurationRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the web ACL for which you want to get the LoggingConfiguration.

    " + } + } + }, + "GetLoggingConfigurationResponse":{ + "type":"structure", + "members":{ + "LoggingConfiguration":{ + "shape":"LoggingConfiguration", + "documentation":"

    The LoggingConfiguration for the specified web ACL.

    " + } + } + }, + "GetPermissionPolicyRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the rule group for which you want to get the policy.

    " + } + } + }, + "GetPermissionPolicyResponse":{ + "type":"structure", + "members":{ + "Policy":{ + "shape":"PolicyString", + "documentation":"

    The IAM policy that is attached to the specified rule group.

    " + } + } + }, + "GetRateBasedStatementManagedKeysRequest":{ + "type":"structure", + "required":[ + "Scope", + "WebACLName", + "WebACLId", + "RuleName" + ], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "WebACLName":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "WebACLId":{ + "shape":"EntityId", + "documentation":"

    The unique identifier for the Web ACL. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "RuleName":{ + "shape":"EntityName", + "documentation":"

    The name of the rate-based rule to get the keys for.

    " + } + } + }, + "GetRateBasedStatementManagedKeysResponse":{ + "type":"structure", + "members":{ + "ManagedKeysIPV4":{ + "shape":"RateBasedStatementManagedKeysIPSet", + "documentation":"

    The keys that are of Internet Protocol version 4 (IPv4).

    " + }, + "ManagedKeysIPV6":{ + "shape":"RateBasedStatementManagedKeysIPSet", + "documentation":"

    The keys that are of Internet Protocol version 6 (IPv6).

    " + } + } + }, + "GetRegexPatternSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the set. You cannot change the name after you create the set.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + } + } + }, + "GetRegexPatternSetResponse":{ + "type":"structure", + "members":{ + "RegexPatternSet":{ + "shape":"RegexPatternSet", + "documentation":"

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "GetRuleGroupRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule group. You cannot change the name of a rule group after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the rule group. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + } + } + }, + "GetRuleGroupResponse":{ + "type":"structure", + "members":{ + "RuleGroup":{ + "shape":"RuleGroup", + "documentation":"

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "GetSampledRequestsRequest":{ + "type":"structure", + "required":[ + "WebAclArn", + "RuleMetricName", + "Scope", + "TimeWindow", + "MaxItems" + ], + "members":{ + "WebAclArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon resource name (ARN) of the WebACL for which you want a sample of requests.

    " + }, + "RuleMetricName":{ + "shape":"MetricName", + "documentation":"

    The metric name assigned to the Rule or RuleGroup for which you want a sample of requests.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "TimeWindow":{ + "shape":"TimeWindow", + "documentation":"

    The start date and time and the end date and time of the range for which you want GetSampledRequests to return a sample of requests. Specify the date and time in the following format: \"2016-09-27T14:50Z\". You can specify any time range in the previous three hours.

    " + }, + "MaxItems":{ + "shape":"ListMaxItems", + "documentation":"

    The number of requests that you want AWS WAF to return from among the first 5,000 requests that your AWS resource received during the time range. If your resource received fewer requests than the value of MaxItems, GetSampledRequests returns information about all of them.

    " + } + } + }, + "GetSampledRequestsResponse":{ + "type":"structure", + "members":{ + "SampledRequests":{ + "shape":"SampledHTTPRequests", + "documentation":"

    A complex type that contains detailed information about each of the requests in the sample.

    " + }, + "PopulationSize":{ + "shape":"PopulationSize", + "documentation":"

    The total number of requests from which GetSampledRequests got a sample of MaxItems requests. If PopulationSize is less than MaxItems, the sample includes every request that your AWS resource received during the specified time range.

    " + }, + "TimeWindow":{ + "shape":"TimeWindow", + "documentation":"

    Usually, TimeWindow is the time range that you specified in the GetSampledRequests request. However, if your AWS resource received more than 5,000 requests during the time range that you specified in the request, GetSampledRequests returns the time range for the first 5,000 requests.

    " + } + } + }, + "GetWebACLForResourceRequest":{ + "type":"structure", + "required":["ResourceArn"], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The ARN (Amazon Resource Name) of the resource.

    " + } + } + }, + "GetWebACLForResourceResponse":{ + "type":"structure", + "members":{ + "WebACL":{ + "shape":"WebACL", + "documentation":"

    The Web ACL that is associated with the resource. If there is no associated resource, AWS WAF returns a null Web ACL.

    " + } + } + }, + "GetWebACLRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    The unique identifier for the Web ACL. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + } + } + }, + "GetWebACLResponse":{ + "type":"structure", + "members":{ + "WebACL":{ + "shape":"WebACL", + "documentation":"

    The Web ACL specification. You can modify the settings in this Web ACL and use it to update this Web ACL or create a new one.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "HTTPHeader":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"HeaderName", + "documentation":"

    The name of the HTTP header.

    " + }, + "Value":{ + "shape":"HeaderValue", + "documentation":"

    The value of the HTTP header.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Part of the response from GetSampledRequests. This is a complex type that appears as Headers in the response syntax. HTTPHeader contains the names and values of all of the headers that appear in one of the web requests.

    " + }, + "HTTPHeaders":{ + "type":"list", + "member":{"shape":"HTTPHeader"} + }, + "HTTPMethod":{"type":"string"}, + "HTTPRequest":{ + "type":"structure", + "members":{ + "ClientIP":{ + "shape":"IPString", + "documentation":"

    The IP address that the request originated from. If the web ACL is associated with a CloudFront distribution, this is the value of one of the following fields in CloudFront access logs:

    • c-ip, if the viewer did not use an HTTP proxy or a load balancer to send the request

    • x-forwarded-for, if the viewer did use an HTTP proxy or a load balancer to send the request

    " + }, + "Country":{ + "shape":"Country", + "documentation":"

    The two-letter country code for the country that the request originated from. For a current list of country codes, see the Wikipedia entry ISO 3166-1 alpha-2.

    " + }, + "URI":{ + "shape":"URIString", + "documentation":"

    The URI path of the request, which identifies the resource, for example, /images/daily-ad.jpg.

    " + }, + "Method":{ + "shape":"HTTPMethod", + "documentation":"

    The HTTP method specified in the sampled web request.

    " + }, + "HTTPVersion":{ + "shape":"HTTPVersion", + "documentation":"

    The HTTP version specified in the sampled web request, for example, HTTP/1.1.

    " + }, + "Headers":{ + "shape":"HTTPHeaders", + "documentation":"

    A complex type that contains the name and value for each header in the sampled web request.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Part of the response from GetSampledRequests. This is a complex type that appears as Request in the response syntax. HTTPRequest contains information about one of the web requests.

    " + }, + "HTTPVersion":{"type":"string"}, + "HeaderName":{"type":"string"}, + "HeaderValue":{"type":"string"}, + "IPAddress":{ + "type":"string", + "max":50, + "min":1, + "pattern":".*\\S.*" + }, + "IPAddressVersion":{ + "type":"string", + "enum":[ + "IPV4", + "IPV6" + ] + }, + "IPAddresses":{ + "type":"list", + "member":{"shape":"IPAddress"} + }, + "IPSet":{ + "type":"structure", + "required":[ + "Name", + "Id", + "ARN", + "IPAddressVersion", + "Addresses" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the IP set. You cannot change the name of an IPSet after you create it.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the IP set that helps with identification. You cannot change the description of an IP set after you create it.

    " + }, + "IPAddressVersion":{ + "shape":"IPAddressVersion", + "documentation":"

    Specify IPV4 or IPV6.

    " + }, + "Addresses":{ + "shape":"IPAddresses", + "documentation":"

    Contains an array of strings that specify one or more IP addresses or blocks of IP addresses in Classless Inter-Domain Routing (CIDR) notation. AWS WAF supports all address ranges for IP versions IPv4 and IPv6.

    Examples:

    • To configure AWS WAF to allow, block, or count requests that originated from the IP address 192.0.2.44, specify 192.0.2.44/32.

    • To configure AWS WAF to allow, block, or count requests that originated from IP addresses from 192.0.2.0 to 192.0.2.255, specify 192.0.2.0/24.

    • To configure AWS WAF to allow, block, or count requests that originated from the IP address 1111:0000:0000:0000:0000:0000:0000:0111, specify 1111:0000:0000:0000:0000:0000:0000:0111/128.

    • To configure AWS WAF to allow, block, or count requests that originated from IP addresses 1111:0000:0000:0000:0000:0000:0000:0000 to 1111:0000:0000:0000:ffff:ffff:ffff:ffff, specify 1111:0000:0000:0000:0000:0000:0000:0000/64.

    For more information about CIDR notation, see the Wikipedia entry Classless Inter-Domain Routing.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Contains one or more IP addresses or blocks of IP addresses specified in Classless Inter-Domain Routing (CIDR) notation. AWS WAF supports any CIDR range. For information about CIDR notation, see the Wikipedia entry Classless Inter-Domain Routing.

    AWS WAF assigns an ARN to each IPSet that you create. To use an IP set in a rule, you provide the ARN to the Rule statement IPSetReferenceStatement.

    " + }, + "IPSetReferenceStatement":{ + "type":"structure", + "required":["ARN"], + "members":{ + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the IPSet that this statement references.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement used to detect web requests coming from particular IP addresses or address ranges. To use this, create an IPSet that specifies the addresses you want to detect, then use the ARN of that set in this statement. To create an IP set, see CreateIPSet.

    Each IP set rule statement references an IP set. You create and maintain the set independent of your rules. This allows you to use the single set in multiple rules. When you update the referenced set, AWS WAF automatically updates all rules that reference it.

    " + }, + "IPSetSummaries":{ + "type":"list", + "member":{"shape":"IPSetSummary"} + }, + "IPSetSummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the IP set. You cannot change the name of an IPSet after you create it.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the IP set that helps with identification. You cannot change the description of an IP set after you create it.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    High-level information about an IPSet, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage an IPSet, and the ARN, that you provide to the IPSetReferenceStatement to use the address set in a Rule.

    " + }, + "IPString":{"type":"string"}, + "ListAvailableManagedRuleGroupsRequest":{ + "type":"structure", + "required":["Scope"], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + } + } + }, + "ListAvailableManagedRuleGroupsResponse":{ + "type":"structure", + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "ManagedRuleGroups":{ + "shape":"ManagedRuleGroupSummaries", + "documentation":"

    " + } + } + }, + "ListIPSetsRequest":{ + "type":"structure", + "required":["Scope"], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + } + } + }, + "ListIPSetsResponse":{ + "type":"structure", + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "IPSets":{ + "shape":"IPSetSummaries", + "documentation":"

    Array of IPSets. This may not be the full list of IPSets that you have defined. See the Limit specification for this request.

    " + } + } + }, + "ListLoggingConfigurationsRequest":{ + "type":"structure", + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + } + } + }, + "ListLoggingConfigurationsResponse":{ + "type":"structure", + "members":{ + "LoggingConfigurations":{ + "shape":"LoggingConfigurations", + "documentation":"

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + } + } + }, + "ListMaxItems":{ + "type":"long", + "max":500, + "min":1 + }, + "ListRegexPatternSetsRequest":{ + "type":"structure", + "required":["Scope"], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + } + } + }, + "ListRegexPatternSetsResponse":{ + "type":"structure", + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "RegexPatternSets":{ + "shape":"RegexPatternSetSummaries", + "documentation":"

    " + } + } + }, + "ListResourcesForWebACLRequest":{ + "type":"structure", + "required":["WebACLArn"], + "members":{ + "WebACLArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the Web ACL.

    " + }, + "ResourceType":{ + "shape":"ResourceType", + "documentation":"

    Used for web ACLs that are scoped for regional applications. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    " + } + } + }, + "ListResourcesForWebACLResponse":{ + "type":"structure", + "members":{ + "ResourceArns":{ + "shape":"ResourceArns", + "documentation":"

    The array of Amazon Resource Names (ARNs) of the associated resources.

    " + } + } + }, + "ListRuleGroupsRequest":{ + "type":"structure", + "required":["Scope"], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + } + } + }, + "ListRuleGroupsResponse":{ + "type":"structure", + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "RuleGroups":{ + "shape":"RuleGroupSummaries", + "documentation":"

    " + } + } + }, + "ListTagsForResourceRequest":{ + "type":"structure", + "required":["ResourceARN"], + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + }, + "ResourceARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + } + } + }, + "ListTagsForResourceResponse":{ + "type":"structure", + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "TagInfoForResource":{ + "shape":"TagInfoForResource", + "documentation":"

    The collection of tagging definitions for the resource.

    " + } + } + }, + "ListWebACLsRequest":{ + "type":"structure", + "required":["Scope"], + "members":{ + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "Limit":{ + "shape":"PaginationLimit", + "documentation":"

    The maximum number of objects that you want AWS WAF to return for this request. If more objects are available, in the response, AWS WAF provides a NextMarker value that you can use in a subsequent call to get the next batch of objects.

    " + } + } + }, + "ListWebACLsResponse":{ + "type":"structure", + "members":{ + "NextMarker":{ + "shape":"NextMarker", + "documentation":"

    When you request a list of objects with a Limit setting, if the number of objects that are still available for retrieval exceeds the limit, AWS WAF returns a NextMarker value in the response. To retrieve the next batch of objects, provide the marker from the prior call in your next request.

    " + }, + "WebACLs":{ + "shape":"WebACLSummaries", + "documentation":"

    " + } + } + }, + "LockToken":{ + "type":"string", + "max":36, + "min":1, + "pattern":"^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$" + }, + "LogDestinationConfigs":{ + "type":"list", + "member":{"shape":"ResourceArn"}, + "max":100, + "min":1 + }, + "LoggingConfiguration":{ + "type":"structure", + "required":[ + "ResourceArn", + "LogDestinationConfigs" + ], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the web ACL that you want to associate with LogDestinationConfigs.

    " + }, + "LogDestinationConfigs":{ + "shape":"LogDestinationConfigs", + "documentation":"

    The Amazon Kinesis Data Firehose Amazon Resource Name (ARNs) that you want to associate with the web ACL.

    " + }, + "RedactedFields":{ + "shape":"RedactedFields", + "documentation":"

    The parts of the request that you want to keep out of the logs. For example, if you redact the cookie field, the cookie field in the firehose will be xxx.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Defines an association between Amazon Kinesis Data Firehose destinations and a web ACL resource, for logging from AWS WAF. As part of the association, you can specify parts of the standard logging fields to keep out of the logs.

    " + }, + "LoggingConfigurations":{ + "type":"list", + "member":{"shape":"LoggingConfiguration"} + }, + "ManagedRuleGroupStatement":{ + "type":"structure", + "required":[ + "VendorName", + "Name" + ], + "members":{ + "VendorName":{ + "shape":"VendorName", + "documentation":"

    The name of the managed rule group vendor. You use this, along with the rule group name, to identify the rule group.

    " + }, + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the managed rule group. You use this, along with the vendor name, to identify the rule group.

    " + }, + "ExcludedRules":{ + "shape":"ExcludedRules", + "documentation":"

    The rules whose actions are set to COUNT by the web ACL, regardless of the action that is set on the rule. This effectively excludes the rule from acting on web requests.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement used to run the rules that are defined in a managed rule group. To use this, provide the vendor name and the name of the rule group in this statement. You can retrieve the required names by calling ListAvailableManagedRuleGroups.

    You can't nest a ManagedRuleGroupStatement, for example for use inside a NotStatement or OrStatement. It can only be referenced as a top-level statement within a rule.

    " + }, + "ManagedRuleGroupSummaries":{ + "type":"list", + "member":{"shape":"ManagedRuleGroupSummary"} + }, + "ManagedRuleGroupSummary":{ + "type":"structure", + "members":{ + "VendorName":{ + "shape":"VendorName", + "documentation":"

    The name of the managed rule group vendor. You use this, along with the rule group name, to identify the rule group.

    " + }, + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the managed rule group. You use this, along with the vendor name, to identify the rule group.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    The description of the managed rule group, provided by AWS Managed Rules or the AWS Marketplace seller who manages it.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    High-level information about a managed rule group, returned by ListAvailableManagedRuleGroups. This provides information like the name and vendor name, that you provide when you add a ManagedRuleGroupStatement to a web ACL. Managed rule groups include AWS Managed Rules rule groups, which are free of charge to AWS WAF customers, and AWS Marketplace managed rule groups, which you can subscribe to through AWS Marketplace.

    " + }, + "Method":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The HTTP method of a web request. The method indicates the type of operation that the request is asking the origin to perform.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "MetricName":{ + "type":"string", + "max":255, + "min":1, + "pattern":"^[\\w#:\\.\\-/]+$" + }, + "NextMarker":{ + "type":"string", + "max":256, + "min":1, + "pattern":".*\\S.*" + }, + "NoneAction":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Specifies that AWS WAF should do nothing. This is generally used to try out a rule without performing any actions. You set the OverrideAction on the Rule.

    This is used only in the context of other settings, for example to specify values for RuleAction and web ACL DefaultAction.

    " + }, + "NotStatement":{ + "type":"structure", + "required":["Statement"], + "members":{ + "Statement":{ + "shape":"Statement", + "documentation":"

    The statement to negate. You can use any statement that can be nested.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A logical rule statement used to negate the results of another rule statement. You provide one Statement within the NotStatement.

    " + }, + "OrStatement":{ + "type":"structure", + "required":["Statements"], + "members":{ + "Statements":{ + "shape":"Statements", + "documentation":"

    The statements to combine with OR logic. You can use any statements that can be nested.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A logical rule statement used to combine other rule statements with OR logic. You provide more than one Statement within the OrStatement.

    " + }, + "OverrideAction":{ + "type":"structure", + "members":{ + "Count":{ + "shape":"CountAction", + "documentation":"

    Override the rule action setting to count.

    " + }, + "None":{ + "shape":"NoneAction", + "documentation":"

    Don't override the rule action setting.

    " + } + }, + "documentation":"

    The override action to apply to the rules in a rule group. Used only for rule statements that reference a rule group, like RuleGroupReferenceStatement and ManagedRuleGroupStatement.

    Set the override action to none to leave the rule actions in effect. Set it to count to only count matches, regardless of the rule action settings.

    In a Rule, you must specify either this OverrideAction setting or the rule Action setting, but not both:

    • If the rule statement references a rule group, use this override action setting and not the action setting.

    • If the rule statement does not reference a rule group, use the rule action setting and not this rule override action setting.

    " + }, + "PaginationLimit":{ + "type":"integer", + "max":100, + "min":1 + }, + "ParameterExceptionField":{ + "type":"string", + "enum":[ + "WEB_ACL", + "RULE_GROUP", + "REGEX_PATTERN_SET", + "IP_SET", + "MANAGED_RULE_SET", + "RULE", + "EXCLUDED_RULE", + "STATEMENT", + "BYTE_MATCH_STATEMENT", + "SQLI_MATCH_STATEMENT", + "XSS_MATCH_STATEMENT", + "SIZE_CONSTRAINT_STATEMENT", + "GEO_MATCH_STATEMENT", + "RATE_BASED_STATEMENT", + "RULE_GROUP_REFERENCE_STATEMENT", + "REGEX_PATTERN_REFERENCE_STATEMENT", + "IP_SET_REFERENCE_STATEMENT", + "MANAGED_RULE_SET_STATEMENT", + "AND_STATEMENT", + "OR_STATEMENT", + "NOT_STATEMENT", + "IP_ADDRESS", + "IP_ADDRESS_VERSION", + "FIELD_TO_MATCH", + "TEXT_TRANSFORMATION", + "SINGLE_QUERY_ARGUMENT", + "SINGLE_HEADER", + "DEFAULT_ACTION", + "RULE_ACTION", + "ENTITY_LIMIT", + "OVERRIDE_ACTION", + "SCOPE_VALUE", + "RESOURCE_ARN", + "RESOURCE_TYPE", + "TAGS", + "TAG_KEYS", + "METRIC_NAME", + "FIREWALL_MANAGER_STATEMENT" + ] + }, + "ParameterExceptionParameter":{ + "type":"string", + "min":1 + }, + "PolicyString":{ + "type":"string", + "min":1 + }, + "PopulationSize":{"type":"long"}, + "PositionalConstraint":{ + "type":"string", + "enum":[ + "EXACTLY", + "STARTS_WITH", + "ENDS_WITH", + "CONTAINS", + "CONTAINS_WORD" + ] + }, + "PutLoggingConfigurationRequest":{ + "type":"structure", + "required":["LoggingConfiguration"], + "members":{ + "LoggingConfiguration":{ + "shape":"LoggingConfiguration", + "documentation":"

    " + } + } + }, + "PutLoggingConfigurationResponse":{ + "type":"structure", + "members":{ + "LoggingConfiguration":{ + "shape":"LoggingConfiguration", + "documentation":"

    " + } + } + }, + "PutPermissionPolicyRequest":{ + "type":"structure", + "required":[ + "ResourceArn", + "Policy" + ], + "members":{ + "ResourceArn":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the RuleGroup to which you want to attach the policy.

    " + }, + "Policy":{ + "shape":"PolicyString", + "documentation":"

    The policy to attach to the specified rule group.

    The policy specifications must conform to the following:

    • The policy must be composed using IAM Policy version 2012-10-17 or version 2015-01-01.

    • The policy must include specifications for Effect, Action, and Principal.

    • Effect must specify Allow.

    • Action must specify wafv2:CreateWebACL, wafv2:UpdateWebACL, and wafv2:PutFirewallManagerRuleGroups. AWS WAF rejects any extra actions or wildcard actions in the policy.

    • The policy must not include a Resource parameter.

    For more information, see IAM Policies.

    " + } + } + }, + "PutPermissionPolicyResponse":{ + "type":"structure", + "members":{ + } + }, + "QueryString":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The query string of a web request. This is the part of a URL that appears after a ? character, if any.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "RateBasedStatement":{ + "type":"structure", + "required":[ + "Limit", + "AggregateKeyType" + ], + "members":{ + "Limit":{ + "shape":"RateLimit", + "documentation":"

    The limit on requests per 5-minute period for a single originating IP address. If the statement includes a ScopDownStatement, this limit is applied only to the requests that match the statement.

    " + }, + "AggregateKeyType":{ + "shape":"RateBasedStatementAggregateKeyType", + "documentation":"

    Setting that indicates how to aggregate the request counts. Currently, you must set this to IP. The request counts are aggregated on IP addresses.

    " + }, + "ScopeDownStatement":{ + "shape":"Statement", + "documentation":"

    An optional nested statement that narrows the scope of the rate-based statement to matching web requests. This can be any nestable statement, and you can nest statements at any level below this scope-down statement.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action when the rate exceeds a limit that you specify on the number of requests in any 5-minute time span. You can use this to put a temporary block on requests from an IP address that is sending excessive requests.

    When the rule action triggers, AWS WAF blocks additional requests from the IP address until the request rate falls below the limit.

    You can optionally nest another statement inside the rate-based statement, to narrow the scope of the rule so that it only counts requests that match the nested statement. For example, based on recent requests that you have seen from an attacker, you might create a rate-based rule with a nested AND rule statement that contains the following nested statements:

    • An IP match statement with an IP set that specified the address 192.0.2.44.

    • A string match statement that searches in the User-Agent header for the string BadBot.

    In this rate-based rule, you also define a rate limit. For this example, the rate limit is 1,000. Requests that meet both of the conditions in the statements are counted. If the count exceeds 1,000 requests per five minutes, the rule action triggers. Requests that do not meet both conditions are not counted towards the rate limit and are not affected by this rule.

    You cannot nest a RateBasedStatement, for example for use inside a NotStatement or OrStatement. It can only be referenced as a top-level statement within a rule.

    " + }, + "RateBasedStatementAggregateKeyType":{ + "type":"string", + "enum":["IP"] + }, + "RateBasedStatementManagedKeysIPSet":{ + "type":"structure", + "members":{ + "IPAddressVersion":{"shape":"IPAddressVersion"}, + "Addresses":{ + "shape":"IPAddresses", + "documentation":"

    The IP addresses that are currently blocked.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The set of IP addresses that are currently blocked for a rate-based statement.

    " + }, + "RateLimit":{ + "type":"long", + "max":2000000000, + "min":100 + }, + "RedactedFields":{ + "type":"list", + "member":{"shape":"FieldToMatch"}, + "max":100 + }, + "Regex":{ + "type":"structure", + "members":{ + "RegexString":{ + "shape":"RegexPatternString", + "documentation":"

    The string representing the regular expression.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A single regular expression. This is used in a RegexPatternSet.

    " + }, + "RegexPatternSet":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the set. You cannot change the name after you create the set.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the set that helps with identification. You cannot change the description of a set after you create it.

    " + }, + "RegularExpressionList":{ + "shape":"RegularExpressionList", + "documentation":"

    The regular expression patterns in the set.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Contains one or more regular expressions.

    AWS WAF assigns an ARN to each RegexPatternSet that you create. To use a set in a rule, you provide the ARN to the Rule statement RegexPatternSetReferenceStatement.

    " + }, + "RegexPatternSetReferenceStatement":{ + "type":"structure", + "required":[ + "ARN", + "FieldToMatch", + "TextTransformations" + ], + "members":{ + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the RegexPatternSet that this statement references.

    " + }, + "FieldToMatch":{ + "shape":"FieldToMatch", + "documentation":"

    The part of a web request that you want AWS WAF to inspect. For more information, see FieldToMatch.

    " + }, + "TextTransformations":{ + "shape":"TextTransformations", + "documentation":"

    Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. If you specify one or more transformations in a rule statement, AWS WAF performs all transformations on the content of the request component identified by FieldToMatch, starting from the lowest priority setting, before inspecting the content for a match.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement used to search web request components for matches with regular expressions. To use this, create a RegexPatternSet that specifies the expressions that you want to detect, then use the ARN of that set in this statement. A web request matches the pattern set rule statement if the request component matches any of the patterns in the set. To create a regex pattern set, see CreateRegexPatternSet.

    Each regex pattern set rule statement references a regex pattern set. You create and maintain the set independent of your rules. This allows you to use the single set in multiple rules. When you update the referenced set, AWS WAF automatically updates all rules that reference it.

    " + }, + "RegexPatternSetSummaries":{ + "type":"list", + "member":{"shape":"RegexPatternSetSummary"} + }, + "RegexPatternSetSummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the data type instance. You cannot change the name after you create the instance.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the set that helps with identification. You cannot change the description of a set after you create it.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    High-level information about a RegexPatternSet, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage a RegexPatternSet, and the ARN, that you provide to the RegexPatternSetReferenceStatement to use the pattern set in a Rule.

    " + }, + "RegexPatternString":{ + "type":"string", + "max":512, + "min":1, + "pattern":".*" + }, + "RegularExpressionList":{ + "type":"list", + "member":{"shape":"Regex"} + }, + "ResourceArn":{ + "type":"string", + "max":2048, + "min":20, + "pattern":".*\\S.*" + }, + "ResourceArns":{ + "type":"list", + "member":{"shape":"ResourceArn"} + }, + "ResourceType":{ + "type":"string", + "enum":[ + "APPLICATION_LOAD_BALANCER", + "API_GATEWAY" + ] + }, + "Rule":{ + "type":"structure", + "required":[ + "Name", + "Priority", + "Statement", + "VisibilityConfig" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule. You can't change the name of a Rule after you create it.

    " + }, + "Priority":{ + "shape":"RulePriority", + "documentation":"

    If you define more than one Rule in a WebACL, AWS WAF evaluates each request against the Rules in order based on the value of Priority. AWS WAF processes rules with lower priority first. The priorities don't need to be consecutive, but they must all be different.

    " + }, + "Statement":{ + "shape":"Statement", + "documentation":"

    The AWS WAF processing statement for the rule, for example ByteMatchStatement or SizeConstraintStatement.

    " + }, + "Action":{ + "shape":"RuleAction", + "documentation":"

    The action that AWS WAF should take on a web request when it matches the rule statement. Settings at the web ACL level can override the rule action setting.

    This is used only for rules whose statements do not reference a rule group. Rule statements that reference a rule group include RuleGroupReferenceStatement and ManagedRuleGroupStatement.

    You must specify either this Action setting or the rule OverrideAction setting, but not both:

    • If the rule statement does not reference a rule group, use this rule action setting and not the rule override action setting.

    • If the rule statement references a rule group, use the override action setting and not this action setting.

    " + }, + "OverrideAction":{ + "shape":"OverrideAction", + "documentation":"

    The override action to apply to the rules in a rule group. Used only for rule statements that reference a rule group, like RuleGroupReferenceStatement and ManagedRuleGroupStatement.

    Set the override action to none to leave the rule actions in effect. Set it to count to only count matches, regardless of the rule action settings.

    In a Rule, you must specify either this OverrideAction setting or the rule Action setting, but not both:

    • If the rule statement references a rule group, use this override action setting and not the action setting.

    • If the rule statement does not reference a rule group, use the rule action setting and not this rule override action setting.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A single rule, which you can use in a WebACL or RuleGroup to identify web requests that you want to allow, block, or count. Each rule includes one top-level Statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "RuleAction":{ + "type":"structure", + "members":{ + "Block":{ + "shape":"BlockAction", + "documentation":"

    Instructs AWS WAF to block the web request.

    " + }, + "Allow":{ + "shape":"AllowAction", + "documentation":"

    Instructs AWS WAF to allow the web request.

    " + }, + "Count":{ + "shape":"CountAction", + "documentation":"

    Instructs AWS WAF to count the web request and allow it.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The action that AWS WAF should take on a web request when it matches a rule's statement. Settings at the web ACL level can override the rule action setting.

    " + }, + "RuleGroup":{ + "type":"structure", + "required":[ + "Name", + "Id", + "Capacity", + "ARN", + "VisibilityConfig" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule group. You cannot change the name of a rule group after you create it.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the rule group. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Capacity":{ + "shape":"CapacityUnit", + "documentation":"

    The web ACL capacity units (WCUs) required for this rule group.

    When you create your own rule group, you define this, and you cannot change it after creation. When you add or modify the rules in a rule group, AWS WAF enforces this limit. You can check the capacity for a set of rules using CheckCapacity.

    AWS WAF uses WCUs to calculate and control the operating resources that are used to run your rules, rule groups, and web ACLs. AWS WAF calculates capacity differently for each rule type, to reflect the relative cost of each rule. Simple rules that cost little to run use fewer WCUs than more complex rules that use more processing power. Rule group capacity is fixed at creation, which helps users plan their web ACL WCU usage when they use a rule group. The WCU limit for web ACLs is 1,500.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the rule group that helps with identification. You cannot change the description of a rule group after you create it.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    The Rule statements used to identify the web requests that you want to allow, block, or count. Each rule includes one top-level statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule group defines a collection of rules to inspect and control web requests that you can use in a WebACL. When you create a rule group, you define an immutable capacity limit. If you update a rule group, you must stay within the capacity. This allows others to reuse the rule group with confidence in its capacity requirements.

    " + }, + "RuleGroupReferenceStatement":{ + "type":"structure", + "required":["ARN"], + "members":{ + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + }, + "ExcludedRules":{ + "shape":"ExcludedRules", + "documentation":"

    The names of rules that are in the referenced rule group, but that you want AWS WAF to exclude from processing for this rule statement.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement used to run the rules that are defined in a RuleGroup. To use this, create a rule group with your rules, then provide the ARN of the rule group in this statement.

    You cannot nest a RuleGroupReferenceStatement, for example for use inside a NotStatement or OrStatement. It can only be referenced as a top-level statement within a rule.

    " + }, + "RuleGroupSummaries":{ + "type":"list", + "member":{"shape":"RuleGroupSummary"} + }, + "RuleGroupSummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the data type instance. You cannot change the name after you create the instance.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the rule group. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the rule group that helps with identification. You cannot change the description of a rule group after you create it.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    High-level information about a RuleGroup, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage a RuleGroup, and the ARN, that you provide to the RuleGroupReferenceStatement to use the rule group in a Rule.

    " + }, + "RulePriority":{ + "type":"integer", + "min":0 + }, + "RuleSummaries":{ + "type":"list", + "member":{"shape":"RuleSummary"} + }, + "RuleSummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule.

    " + }, + "Action":{"shape":"RuleAction"} + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    High-level information about a Rule, returned by operations like DescribeManagedRuleGroup. This provides information like the ID, that you can use to retrieve and manage a RuleGroup, and the ARN, that you provide to the RuleGroupReferenceStatement to use the rule group in a Rule.

    " + }, + "Rules":{ + "type":"list", + "member":{"shape":"Rule"} + }, + "SampleWeight":{ + "type":"long", + "min":0 + }, + "SampledHTTPRequest":{ + "type":"structure", + "required":[ + "Request", + "Weight" + ], + "members":{ + "Request":{ + "shape":"HTTPRequest", + "documentation":"

    A complex type that contains detailed information about the request.

    " + }, + "Weight":{ + "shape":"SampleWeight", + "documentation":"

    A value that indicates how one result in the response relates proportionally to other results in the response. For example, a result that has a weight of 2 represents roughly twice as many web requests as a result that has a weight of 1.

    " + }, + "Timestamp":{ + "shape":"Timestamp", + "documentation":"

    The time at which AWS WAF received the request from your AWS resource, in Unix time format (in seconds).

    " + }, + "Action":{ + "shape":"Action", + "documentation":"

    The action for the Rule that the request matched: ALLOW, BLOCK, or COUNT.

    " + }, + "RuleNameWithinRuleGroup":{ + "shape":"EntityName", + "documentation":"

    The name of the Rule that the request matched. For managed rule groups, the format for this name is <vendor name>#<managed rule group name>#<rule name>. For your own rule groups, the format for this name is <rule group name>#<rule name>. If the rule is not in a rule group, the format is <rule name>.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Represents a single sampled web request. The response from GetSampledRequests includes a SampledHTTPRequests complex type that appears as SampledRequests in the response syntax. SampledHTTPRequests contains an array of SampledHTTPRequest objects.

    " + }, + "SampledHTTPRequests":{ + "type":"list", + "member":{"shape":"SampledHTTPRequest"} + }, + "Scope":{ + "type":"string", + "enum":[ + "CLOUDFRONT", + "REGIONAL" + ] + }, + "SearchString":{"type":"blob"}, + "SingleHeader":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"FieldToMatchData", + "documentation":"

    The name of the query header to inspect.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    One of the headers in a web request, identified by name, for example, User-Agent or Referer. This setting isn't case sensitive.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "SingleQueryArgument":{ + "type":"structure", + "required":["Name"], + "members":{ + "Name":{ + "shape":"FieldToMatchData", + "documentation":"

    The name of the query argument to inspect.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    One query argument in a web request, identified by name, for example UserName or SalesRegion. The name can be up to 30 characters long and isn't case sensitive.

    " + }, + "Size":{ + "type":"long", + "max":21474836480, + "min":0 + }, + "SizeConstraintStatement":{ + "type":"structure", + "required":[ + "FieldToMatch", + "ComparisonOperator", + "Size", + "TextTransformations" + ], + "members":{ + "FieldToMatch":{ + "shape":"FieldToMatch", + "documentation":"

    The part of a web request that you want AWS WAF to inspect. For more information, see FieldToMatch.

    " + }, + "ComparisonOperator":{ + "shape":"ComparisonOperator", + "documentation":"

    The operator to use to compare the request part to the size setting.

    " + }, + "Size":{ + "shape":"Size", + "documentation":"

    The size, in byte, to compare to the request part, after any transformations.

    " + }, + "TextTransformations":{ + "shape":"TextTransformations", + "documentation":"

    Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. If you specify one or more transformations in a rule statement, AWS WAF performs all transformations on the content of the request component identified by FieldToMatch, starting from the lowest priority setting, before inspecting the content for a match.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement that compares a number of bytes against the size of a request component, using a comparison operator, such as greater than (>) or less than (<). For example, you can use a size constraint statement to look for query strings that are longer than 100 bytes.

    If you configure AWS WAF to inspect the request body, AWS WAF inspects only the first 8192 bytes (8 KB). If the request body for your web requests never exceeds 8192 bytes, you can create a size constraint condition and block requests that have a request body greater than 8192 bytes.

    If you choose URI for the value of Part of the request to filter on, the slash (/) in the URI counts as one character. For example, the URI /logo.jpg is nine characters long.

    " + }, + "SqliMatchStatement":{ + "type":"structure", + "required":[ + "FieldToMatch", + "TextTransformations" + ], + "members":{ + "FieldToMatch":{ + "shape":"FieldToMatch", + "documentation":"

    The part of a web request that you want AWS WAF to inspect. For more information, see FieldToMatch.

    " + }, + "TextTransformations":{ + "shape":"TextTransformations", + "documentation":"

    Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. If you specify one or more transformations in a rule statement, AWS WAF performs all transformations on the content of the request component identified by FieldToMatch, starting from the lowest priority setting, before inspecting the content for a match.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Attackers sometimes insert malicious SQL code into web requests in an effort to extract data from your database. To allow or block web requests that appear to contain malicious SQL code, create one or more SQL injection match conditions. An SQL injection match condition identifies the part of web requests, such as the URI or the query string, that you want AWS WAF to inspect. Later in the process, when you create a web ACL, you specify whether to allow or block requests that appear to contain malicious SQL code.

    " + }, + "Statement":{ + "type":"structure", + "members":{ + "ByteMatchStatement":{ + "shape":"ByteMatchStatement", + "documentation":"

    A rule statement that defines a string match search for AWS WAF to apply to web requests. The byte match statement provides the bytes to search for, the location in requests that you want AWS WAF to search, and other settings. The bytes to search for are typically a string that corresponds with ASCII characters. In the AWS WAF console and the developer guide, this is refered to as a string match statement.

    " + }, + "SqliMatchStatement":{ + "shape":"SqliMatchStatement", + "documentation":"

    Attackers sometimes insert malicious SQL code into web requests in an effort to extract data from your database. To allow or block web requests that appear to contain malicious SQL code, create one or more SQL injection match conditions. An SQL injection match condition identifies the part of web requests, such as the URI or the query string, that you want AWS WAF to inspect. Later in the process, when you create a web ACL, you specify whether to allow or block requests that appear to contain malicious SQL code.

    " + }, + "XssMatchStatement":{ + "shape":"XssMatchStatement", + "documentation":"

    A rule statement that defines a cross-site scripting (XSS) match search for AWS WAF to apply to web requests. XSS attacks are those where the attacker uses vulnerabilities in a benign website as a vehicle to inject malicious client-site scripts into other legitimate web browsers. The XSS match statement provides the location in requests that you want AWS WAF to search and text transformations to use on the search area before AWS WAF searches for character sequences that are likely to be malicious strings.

    " + }, + "SizeConstraintStatement":{ + "shape":"SizeConstraintStatement", + "documentation":"

    A rule statement that compares a number of bytes against the size of a request component, using a comparison operator, such as greater than (>) or less than (<). For example, you can use a size constraint statement to look for query strings that are longer than 100 bytes.

    If you configure AWS WAF to inspect the request body, AWS WAF inspects only the first 8192 bytes (8 KB). If the request body for your web requests never exceeds 8192 bytes, you can create a size constraint condition and block requests that have a request body greater than 8192 bytes.

    If you choose URI for the value of Part of the request to filter on, the slash (/) in the URI counts as one character. For example, the URI /logo.jpg is nine characters long.

    " + }, + "GeoMatchStatement":{ + "shape":"GeoMatchStatement", + "documentation":"

    A rule statement used to identify web requests based on country of origin.

    " + }, + "RuleGroupReferenceStatement":{ + "shape":"RuleGroupReferenceStatement", + "documentation":"

    A rule statement used to run the rules that are defined in a RuleGroup. To use this, create a rule group with your rules, then provide the ARN of the rule group in this statement.

    You cannot nest a RuleGroupReferenceStatement, for example for use inside a NotStatement or OrStatement. It can only be referenced as a top-level statement within a rule.

    " + }, + "IPSetReferenceStatement":{ + "shape":"IPSetReferenceStatement", + "documentation":"

    A rule statement used to detect web requests coming from particular IP addresses or address ranges. To use this, create an IPSet that specifies the addresses you want to detect, then use the ARN of that set in this statement. To create an IP set, see CreateIPSet.

    Each IP set rule statement references an IP set. You create and maintain the set independent of your rules. This allows you to use the single set in multiple rules. When you update the referenced set, AWS WAF automatically updates all rules that reference it.

    " + }, + "RegexPatternSetReferenceStatement":{ + "shape":"RegexPatternSetReferenceStatement", + "documentation":"

    A rule statement used to search web request components for matches with regular expressions. To use this, create a RegexPatternSet that specifies the expressions that you want to detect, then use the ARN of that set in this statement. A web request matches the pattern set rule statement if the request component matches any of the patterns in the set. To create a regex pattern set, see CreateRegexPatternSet.

    Each regex pattern set rule statement references a regex pattern set. You create and maintain the set independent of your rules. This allows you to use the single set in multiple rules. When you update the referenced set, AWS WAF automatically updates all rules that reference it.

    " + }, + "RateBasedStatement":{ + "shape":"RateBasedStatement", + "documentation":"

    A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action when the rate exceeds a limit that you specify on the number of requests in any 5-minute time span. You can use this to put a temporary block on requests from an IP address that is sending excessive requests.

    When the rule action triggers, AWS WAF blocks additional requests from the IP address until the request rate falls below the limit.

    You can optionally nest another statement inside the rate-based statement, to narrow the scope of the rule so that it only counts requests that match the nested statement. For example, based on recent requests that you have seen from an attacker, you might create a rate-based rule with a nested AND rule statement that contains the following nested statements:

    • An IP match statement with an IP set that specified the address 192.0.2.44.

    • A string match statement that searches in the User-Agent header for the string BadBot.

    In this rate-based rule, you also define a rate limit. For this example, the rate limit is 1,000. Requests that meet both of the conditions in the statements are counted. If the count exceeds 1,000 requests per five minutes, the rule action triggers. Requests that do not meet both conditions are not counted towards the rate limit and are not affected by this rule.

    You cannot nest a RateBasedStatement, for example for use inside a NotStatement or OrStatement. It can only be referenced as a top-level statement within a rule.

    " + }, + "AndStatement":{ + "shape":"AndStatement", + "documentation":"

    A logical rule statement used to combine other rule statements with AND logic. You provide more than one Statement within the AndStatement.

    " + }, + "OrStatement":{ + "shape":"OrStatement", + "documentation":"

    A logical rule statement used to combine other rule statements with OR logic. You provide more than one Statement within the OrStatement.

    " + }, + "NotStatement":{ + "shape":"NotStatement", + "documentation":"

    A logical rule statement used to negate the results of another rule statement. You provide one Statement within the NotStatement.

    " + }, + "ManagedRuleGroupStatement":{ + "shape":"ManagedRuleGroupStatement", + "documentation":"

    A rule statement used to run the rules that are defined in a managed rule group. To use this, provide the vendor name and the name of the rule group in this statement. You can retrieve the required names by calling ListAvailableManagedRuleGroups.

    You can't nest a ManagedRuleGroupStatement, for example for use inside a NotStatement or OrStatement. It can only be referenced as a top-level statement within a rule.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The processing guidance for a Rule, used by AWS WAF to determine whether a web request matches the rule.

    " + }, + "Statements":{ + "type":"list", + "member":{"shape":"Statement"} + }, + "Tag":{ + "type":"structure", + "required":[ + "Key", + "Value" + ], + "members":{ + "Key":{ + "shape":"TagKey", + "documentation":"

    Part of the key:value pair that defines a tag. You can use a tag key to describe a category of information, such as \"customer.\" Tag keys are case-sensitive.

    " + }, + "Value":{ + "shape":"TagValue", + "documentation":"

    Part of the key:value pair that defines a tag. You can use a tag value to describe a specific value within a category, such as \"companyA\" or \"companyB.\" Tag values are case-sensitive.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A collection of key:value pairs associated with an AWS resource. The key:value pair can be anything you define. Typically, the tag key represents a category (such as \"environment\") and the tag value represents a specific value within that category (such as \"test,\" \"development,\" or \"production\"). You can add up to 50 tags to each AWS resource.

    " + }, + "TagInfoForResource":{ + "type":"structure", + "members":{ + "ResourceARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + }, + "TagList":{ + "shape":"TagList", + "documentation":"

    The array of Tag objects defined for the resource.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The collection of tagging definitions for an AWS resource.

    " + }, + "TagKey":{ + "type":"string", + "max":128, + "min":1, + "pattern":".*\\S.*" + }, + "TagKeyList":{ + "type":"list", + "member":{"shape":"TagKey"}, + "min":1 + }, + "TagList":{ + "type":"list", + "member":{"shape":"Tag"}, + "min":1 + }, + "TagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceARN", + "Tags" + ], + "members":{ + "ResourceARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + }, + "Tags":{ + "shape":"TagList", + "documentation":"

    An array of key:value pairs to associate with the resource.

    " + } + } + }, + "TagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "TagValue":{ + "type":"string", + "max":256, + "min":0, + "pattern":".*" + }, + "TextTransformation":{ + "type":"structure", + "required":[ + "Priority", + "Type" + ], + "members":{ + "Priority":{ + "shape":"TextTransformationPriority", + "documentation":"

    Sets the relative processing order for multiple transformations that are defined for a rule statement. AWS WAF processes all transformations, from lowest priority to highest, before inspecting the transformed content. The priorities don't need to be consecutive, but they must all be different.

    " + }, + "Type":{ + "shape":"TextTransformationType", + "documentation":"

    You can specify the following transformation types:

    CMD_LINE

    When you're concerned that attackers are injecting an operating system command line command and using unusual formatting to disguise some or all of the command, use this option to perform the following transformations:

    • Delete the following characters: \\ \" ' ^

    • Delete spaces before the following characters: / (

    • Replace the following characters with a space: , ;

    • Replace multiple spaces with one space

    • Convert uppercase letters (A-Z) to lowercase (a-z)

    COMPRESS_WHITE_SPACE

    Use this option to replace the following characters with a space character (decimal 32):

    • \\f, formfeed, decimal 12

    • \\t, tab, decimal 9

    • \\n, newline, decimal 10

    • \\r, carriage return, decimal 13

    • \\v, vertical tab, decimal 11

    • non-breaking space, decimal 160

    COMPRESS_WHITE_SPACE also replaces multiple spaces with one space.

    HTML_ENTITY_DECODE

    Use this option to replace HTML-encoded characters with unencoded characters. HTML_ENTITY_DECODE performs the following operations:

    • Replaces (ampersand)quot; with \"

    • Replaces (ampersand)nbsp; with a non-breaking space, decimal 160

    • Replaces (ampersand)lt; with a \"less than\" symbol

    • Replaces (ampersand)gt; with >

    • Replaces characters that are represented in hexadecimal format, (ampersand)#xhhhh;, with the corresponding characters

    • Replaces characters that are represented in decimal format, (ampersand)#nnnn;, with the corresponding characters

    LOWERCASE

    Use this option to convert uppercase letters (A-Z) to lowercase (a-z).

    URL_DECODE

    Use this option to decode a URL-encoded value.

    NONE

    Specify NONE if you don't want any text transformations.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection.

    " + }, + "TextTransformationPriority":{ + "type":"integer", + "min":0 + }, + "TextTransformationType":{ + "type":"string", + "enum":[ + "NONE", + "COMPRESS_WHITE_SPACE", + "HTML_ENTITY_DECODE", + "LOWERCASE", + "CMD_LINE", + "URL_DECODE" + ] + }, + "TextTransformations":{ + "type":"list", + "member":{"shape":"TextTransformation"}, + "min":1 + }, + "TimeWindow":{ + "type":"structure", + "required":[ + "StartTime", + "EndTime" + ], + "members":{ + "StartTime":{ + "shape":"Timestamp", + "documentation":"

    The beginning of the time range from which you want GetSampledRequests to return a sample of the requests that your AWS resource received. Specify the date and time in the following format: \"2016-09-27T14:50Z\". You can specify any time range in the previous three hours.

    " + }, + "EndTime":{ + "shape":"Timestamp", + "documentation":"

    The end of the time range from which you want GetSampledRequests to return a sample of the requests that your AWS resource received. Specify the date and time in the following format: \"2016-09-27T14:50Z\". You can specify any time range in the previous three hours.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    In a GetSampledRequests request, the StartTime and EndTime objects specify the time range for which you want AWS WAF to return a sample of web requests.

    In a GetSampledRequests response, the StartTime and EndTime objects specify the time range for which AWS WAF actually returned a sample of web requests. AWS WAF gets the specified number of requests from among the first 5,000 requests that your AWS resource receives during the specified time period. If your resource receives more than 5,000 requests during that period, AWS WAF stops sampling after the 5,000th request. In that case, EndTime is the time that AWS WAF received the 5,000th request.

    " + }, + "Timestamp":{"type":"timestamp"}, + "URIString":{"type":"string"}, + "UntagResourceRequest":{ + "type":"structure", + "required":[ + "ResourceARN", + "TagKeys" + ], + "members":{ + "ResourceARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the resource.

    " + }, + "TagKeys":{ + "shape":"TagKeyList", + "documentation":"

    An array of keys identifying the tags to disassociate from the resource.

    " + } + } + }, + "UntagResourceResponse":{ + "type":"structure", + "members":{ + } + }, + "UpdateIPSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "Addresses", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the IP set. You cannot change the name of an IPSet after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the IP set that helps with identification. You cannot change the description of an IP set after you create it.

    " + }, + "Addresses":{ + "shape":"IPAddresses", + "documentation":"

    Contains an array of strings that specify one or more IP addresses or blocks of IP addresses in Classless Inter-Domain Routing (CIDR) notation. AWS WAF supports all address ranges for IP versions IPv4 and IPv6.

    Examples:

    • To configure AWS WAF to allow, block, or count requests that originated from the IP address 192.0.2.44, specify 192.0.2.44/32.

    • To configure AWS WAF to allow, block, or count requests that originated from IP addresses from 192.0.2.0 to 192.0.2.255, specify 192.0.2.0/24.

    • To configure AWS WAF to allow, block, or count requests that originated from the IP address 1111:0000:0000:0000:0000:0000:0000:0111, specify 1111:0000:0000:0000:0000:0000:0000:0111/128.

    • To configure AWS WAF to allow, block, or count requests that originated from IP addresses 1111:0000:0000:0000:0000:0000:0000:0000 to 1111:0000:0000:0000:ffff:ffff:ffff:ffff, specify 1111:0000:0000:0000:0000:0000:0000:0000/64.

    For more information about CIDR notation, see the Wikipedia entry Classless Inter-Domain Routing.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "UpdateIPSetResponse":{ + "type":"structure", + "members":{ + "NextLockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns this token to your update requests. You use NextLockToken in the same manner as you use LockToken.

    " + } + } + }, + "UpdateRegexPatternSetRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "RegularExpressionList", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the set. You cannot change the name after you create the set.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the set. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the set that helps with identification. You cannot change the description of a set after you create it.

    " + }, + "RegularExpressionList":{ + "shape":"RegularExpressionList", + "documentation":"

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "UpdateRegexPatternSetResponse":{ + "type":"structure", + "members":{ + "NextLockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns this token to your update requests. You use NextLockToken in the same manner as you use LockToken.

    " + } + } + }, + "UpdateRuleGroupRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "VisibilityConfig", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the rule group. You cannot change the name of a rule group after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the rule group. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the rule group that helps with identification. You cannot change the description of a rule group after you create it.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    The Rule statements used to identify the web requests that you want to allow, block, or count. Each rule includes one top-level statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "UpdateRuleGroupResponse":{ + "type":"structure", + "members":{ + "NextLockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns this token to your update requests. You use NextLockToken in the same manner as you use LockToken.

    " + } + } + }, + "UpdateWebACLRequest":{ + "type":"structure", + "required":[ + "Name", + "Scope", + "Id", + "DefaultAction", + "VisibilityConfig", + "LockToken" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "Scope":{ + "shape":"Scope", + "documentation":"

    Specifies whether this is for an AWS CloudFront distribution or for a regional application. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    To work with CloudFront, you must also specify the Region US East (N. Virginia) as follows:

    • CLI - Specify the Region when you use the CloudFront scope: --scope=CLOUDFRONT --region=us-east-1.

    • API and SDKs - For all calls, use the Region endpoint us-east-1.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    The unique identifier for the Web ACL. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "DefaultAction":{ + "shape":"DefaultAction", + "documentation":"

    The action to perform if none of the Rules contained in the WebACL match.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the Web ACL that helps with identification. You cannot change the description of a Web ACL after you create it.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    The Rule statements used to identify the web requests that you want to allow, block, or count. Each rule includes one top-level statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + } + } + }, + "UpdateWebACLResponse":{ + "type":"structure", + "members":{ + "NextLockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns this token to your update requests. You use NextLockToken in the same manner as you use LockToken.

    " + } + } + }, + "UriPath":{ + "type":"structure", + "members":{ + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    The path component of the URI of a web request. This is the part of a web request that identifies a resource, for example, /images/daily-ad.jpg.

    This is used only to indicate the web request component for AWS WAF to inspect, in the FieldToMatch specification.

    " + }, + "VendorName":{ + "type":"string", + "max":128, + "min":1, + "pattern":".*\\S.*" + }, + "VisibilityConfig":{ + "type":"structure", + "required":[ + "SampledRequestsEnabled", + "CloudWatchMetricsEnabled", + "MetricName" + ], + "members":{ + "SampledRequestsEnabled":{ + "shape":"Boolean", + "documentation":"

    A boolean indicating whether AWS WAF should store a sampling of the web requests that match the rules. You can view the sampled requests through the AWS WAF console.

    " + }, + "CloudWatchMetricsEnabled":{ + "shape":"Boolean", + "documentation":"

    A boolean indicating whether the associated resource sends metrics to CloudWatch. For the list of available metrics, see AWS WAF Metrics.

    " + }, + "MetricName":{ + "shape":"MetricName", + "documentation":"

    A name of the CloudWatch metric. The name can contain only alphanumeric characters (A-Z, a-z, 0-9), with length from one to 128 characters. It can't contain whitespace or metric names reserved for AWS WAF, for example \"All\" and \"Default_Action.\" You can't change a MetricName after you create a VisibilityConfig.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + }, + "WAFAssociatedItemException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t perform the operation because your resource is being used by another resource or it’s associated with another resource.

    ", + "exception":true + }, + "WAFDuplicateItemException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t perform the operation because the resource that you tried to save is a duplicate of an existing one.

    ", + "exception":true + }, + "WAFInternalErrorException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    Your request is valid, but AWS WAF couldn’t perform the operation because of a system problem. Retry your request.

    ", + "exception":true, + "fault":true + }, + "WAFInvalidOperationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The operation isn't valid.

    ", + "exception":true + }, + "WAFInvalidParameterException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"}, + "Field":{"shape":"ParameterExceptionField"}, + "Parameter":{"shape":"ParameterExceptionParameter"}, + "Reason":{"shape":"ErrorReason"} + }, + "documentation":"

    The operation failed because AWS WAF didn't recognize a parameter in the request. For example:

    • You specified an invalid parameter name or value.

    • Your nested statement isn't valid. You might have tried to nest a statement that can’t be nested.

    • You tried to update a WebACL with a DefaultAction that isn't among the types available at DefaultAction.

    • Your request references an ARN that is malformed, or corresponds to a resource with which a Web ACL cannot be associated.

    ", + "exception":true + }, + "WAFInvalidPermissionPolicyException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    The operation failed because the specified policy isn't in the proper format.

    The policy specifications must conform to the following:

    • The policy must be composed using IAM Policy version 2012-10-17 or version 2015-01-01.

    • The policy must include specifications for Effect, Action, and Principal.

    • Effect must specify Allow.

    • Action must specify wafv2:CreateWebACL, wafv2:UpdateWebACL, and wafv2:PutFirewallManagerRuleGroups. AWS WAF rejects any extra actions or wildcard actions in the policy.

    • The policy must not include a Resource parameter.

    For more information, see IAM Policies.

    ", + "exception":true + }, + "WAFInvalidResourceException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t perform the operation because the resource that you requested isn’t valid. Check the resource, and try again.

    ", + "exception":true + }, + "WAFLimitsExceededException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t perform the operation because you exceeded your resource limit. For example, the maximum number of WebACL objects that you can create for an AWS account. For more information, see Limits in the AWS WAF Developer Guide.

    ", + "exception":true + }, + "WAFNonexistentItemException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t perform the operation because your resource doesn’t exist.

    ", + "exception":true + }, + "WAFOptimisticLockException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t save your changes because you tried to update or delete a resource that has changed since you last retrieved it. Get the resource again, make any changes you need to make to the new copy, and retry your operation.

    ", + "exception":true + }, + "WAFServiceLinkedRoleErrorException":{ + "type":"structure", + "members":{ + "message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF is not able to access the service linked role. This can be caused by a previous PutLoggingConfiguration request, which can lock the service linked role for about 20 seconds. Please try your request again. The service linked role can also be locked by a previous DeleteServiceLinkedRole request, which can lock the role for 15 minutes or more. If you recently made a call to DeleteServiceLinkedRole, wait at least 15 minutes and try the request again. If you receive this same exception again, you will have to wait additional time until the role is unlocked.

    ", + "exception":true + }, + "WAFSubscriptionNotFoundException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    ", + "exception":true + }, + "WAFTagOperationException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    An error occurred during the tagging operation. Retry your request.

    ", + "exception":true + }, + "WAFTagOperationInternalErrorException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t perform your tagging operation because of an internal error. Retry your request.

    ", + "exception":true, + "fault":true + }, + "WAFUnavailableEntityException":{ + "type":"structure", + "members":{ + "Message":{"shape":"ErrorMessage"} + }, + "documentation":"

    AWS WAF couldn’t retrieve the resource that you requested. Retry your request.

    ", + "exception":true + }, + "WebACL":{ + "type":"structure", + "required":[ + "Name", + "Id", + "ARN", + "DefaultAction", + "VisibilityConfig" + ], + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    A unique identifier for the WebACL. This ID is returned in the responses to create and list commands. You use this ID to do things like get, update, and delete a WebACL.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the Web ACL that you want to associate with the resource.

    " + }, + "DefaultAction":{ + "shape":"DefaultAction", + "documentation":"

    The action to perform if none of the Rules contained in the WebACL match.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the Web ACL that helps with identification. You cannot change the description of a Web ACL after you create it.

    " + }, + "Rules":{ + "shape":"Rules", + "documentation":"

    The Rule statements used to identify the web requests that you want to allow, block, or count. Each rule includes one top-level statement that AWS WAF uses to identify matching web requests, and parameters that govern how AWS WAF handles them.

    " + }, + "VisibilityConfig":{ + "shape":"VisibilityConfig", + "documentation":"

    Defines and enables Amazon CloudWatch metrics and web request sample collection.

    " + }, + "Capacity":{ + "shape":"ConsumedCapacity", + "documentation":"

    The web ACL capacity units (WCUs) currently being used by this web ACL.

    AWS WAF uses WCUs to calculate and control the operating resources that are used to run your rules, rule groups, and web ACLs. AWS WAF calculates capacity differently for each rule type, to reflect the relative cost of each rule. Simple rules that cost little to run use fewer WCUs than more complex rules that use more processing power. Rule group capacity is fixed at creation, which helps users plan their web ACL WCU usage when they use a rule group. The WCU limit for web ACLs is 1,500.

    " + }, + "PreProcessFirewallManagerRuleGroups":{ + "shape":"FirewallManagerRuleGroups", + "documentation":"

    The first set of rules for AWS WAF to process in the web ACL. This is defined in an AWS Firewall Manager WAF policy and contains only rule group references. You can't alter these. Any rules and rule groups that you define for the web ACL are prioritized after these.

    In the Firewall Manager WAF policy, the Firewall Manager administrator can define a set of rule groups to run first in the web ACL and a set of rule groups to run last. Within each set, the administrator prioritizes the rule groups, to determine their relative processing order.

    " + }, + "PostProcessFirewallManagerRuleGroups":{ + "shape":"FirewallManagerRuleGroups", + "documentation":"

    The last set of rules for AWS WAF to process in the web ACL. This is defined in an AWS Firewall Manager WAF policy and contains only rule group references. You can't alter these. Any rules and rule groups that you define for the web ACL are prioritized before these.

    In the Firewall Manager WAF policy, the Firewall Manager administrator can define a set of rule groups to run first in the web ACL and a set of rule groups to run last. Within each set, the administrator prioritizes the rule groups, to determine their relative processing order.

    " + }, + "ManagedByFirewallManager":{ + "shape":"Boolean", + "documentation":"

    Indicates whether this web ACL is managed by AWS Firewall Manager. If true, then only AWS Firewall Manager can delete the web ACL or any Firewall Manager rule groups in the web ACL.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A Web ACL defines a collection of rules to use to inspect and control web requests. Each rule has an action defined (allow, block, or count) for requests that match the statement of the rule. In the Web ACL, you assign a default action to take (allow, block) for any request that does not match any of the rules. The rules in a Web ACL can be a combination of the types Rule, RuleGroup, and managed rule group. You can associate a Web ACL with one or more AWS resources to protect. The resources can be Amazon CloudFront, an Amazon API Gateway API, or an Application Load Balancer.

    " + }, + "WebACLSummaries":{ + "type":"list", + "member":{"shape":"WebACLSummary"} + }, + "WebACLSummary":{ + "type":"structure", + "members":{ + "Name":{ + "shape":"EntityName", + "documentation":"

    The name of the Web ACL. You cannot change the name of a Web ACL after you create it.

    " + }, + "Id":{ + "shape":"EntityId", + "documentation":"

    The unique identifier for the Web ACL. This ID is returned in the responses to create and list commands. You provide it to operations like update and delete.

    " + }, + "Description":{ + "shape":"EntityDescription", + "documentation":"

    A description of the Web ACL that helps with identification. You cannot change the description of a Web ACL after you create it.

    " + }, + "LockToken":{ + "shape":"LockToken", + "documentation":"

    A token used for optimistic locking. AWS WAF returns a token to your get and list requests, to mark the state of the entity at the time of the request. To make changes to the entity associated with the token, you provide the token to operations like update and delete. AWS WAF uses the token to ensure that no changes have been made to the entity since you last retrieved it. If a change has been made, the update fails with a WAFOptimisticLockException. If this happens, perform another get, and use the new token returned by that operation.

    " + }, + "ARN":{ + "shape":"ResourceArn", + "documentation":"

    The Amazon Resource Name (ARN) of the entity.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    High-level information about a WebACL, returned by operations like create and list. This provides information like the ID, that you can use to retrieve and manage a WebACL, and the ARN, that you provide to operations like AssociateWebACL.

    " + }, + "XssMatchStatement":{ + "type":"structure", + "required":[ + "FieldToMatch", + "TextTransformations" + ], + "members":{ + "FieldToMatch":{ + "shape":"FieldToMatch", + "documentation":"

    The part of a web request that you want AWS WAF to inspect. For more information, see FieldToMatch.

    " + }, + "TextTransformations":{ + "shape":"TextTransformations", + "documentation":"

    Text transformations eliminate some of the unusual formatting that attackers use in web requests in an effort to bypass detection. If you specify one or more transformations in a rule statement, AWS WAF performs all transformations on the content of the request component identified by FieldToMatch, starting from the lowest priority setting, before inspecting the content for a match.

    " + } + }, + "documentation":"

    This is the latest version of AWS WAF, named AWS WAFV2, released in November, 2019. For information, including how to migrate your AWS WAF resources from the prior release, see the AWS WAF Developer Guide.

    A rule statement that defines a cross-site scripting (XSS) match search for AWS WAF to apply to web requests. XSS attacks are those where the attacker uses vulnerabilities in a benign website as a vehicle to inject malicious client-site scripts into other legitimate web browsers. The XSS match statement provides the location in requests that you want AWS WAF to search and text transformations to use on the search area before AWS WAF searches for character sequences that are likely to be malicious strings.

    " + } + }, + "documentation":"

    This is the latest version of the AWS WAF API, released in November, 2019. The names of the entities that you use to access this API, like endpoints and namespaces, all have the versioning information added, like \"V2\" or \"v2\", to distinguish from the prior version. We recommend migrating your resources to this version, because it has a number of significant improvements.

    If you used AWS WAF prior to this release, you can't use this AWS WAFV2 API to access any AWS WAF resources that you created before. You can access your old rules, web ACLs, and other AWS WAF resources only through the AWS WAF Classic APIs. The AWS WAF Classic APIs have retained the prior names, endpoints, and namespaces.

    For information, including how to migrate your AWS WAF resources to this version, see the AWS WAF Developer Guide.

    AWS WAF is a web application firewall that lets you monitor the HTTP and HTTPS requests that are forwarded to Amazon CloudFront, an Amazon API Gateway API, or an Application Load Balancer. AWS WAF also lets you control access to your content. Based on conditions that you specify, such as the IP addresses that requests originate from or the values of query strings, API Gateway, CloudFront, or the Application Load Balancer responds to requests either with the requested content or with an HTTP 403 status code (Forbidden). You also can configure CloudFront to return a custom error page when a request is blocked.

    This API guide is for developers who need detailed information about AWS WAF API actions, data types, and errors. For detailed information about AWS WAF features and an overview of how to use AWS WAF, see the AWS WAF Developer Guide.

    You can make calls using the endpoints listed in AWS Service Endpoints for AWS WAF.

    • For regional applications, you can use any of the endpoints in the list. A regional application can be an Application Load Balancer (ALB) or an API Gateway stage.

    • For AWS CloudFront applications, you must use the API endpoint listed for US East (N. Virginia): us-east-1.

    Alternatively, you can use one of the AWS SDKs to access an API that's tailored to the programming language or platform that you're using. For more information, see AWS SDKs.

    We currently provide two versions of the AWS WAF API: this API and the prior versions, the classic AWS WAF APIs. This new API provides the same functionality as the older versions, with the following major improvements:

    • You use one API for both global and regional applications. Where you need to distinguish the scope, you specify a Scope parameter and set it to CLOUDFRONT or REGIONAL.

    • You can define a Web ACL or rule group with a single call, and update it with a single call. You define all rule specifications in JSON format, and pass them to your rule group or Web ACL calls.

    • The limits AWS WAF places on the use of rules more closely reflects the cost of running each type of rule. Rule groups include capacity settings, so you know the maximum cost of a rule group when you use it.

    " +} diff --git a/services/workdocs/build.properties b/services/workdocs/build.properties index ecf2dae6fcb1..15ec2da1fc71 100644 --- a/services/workdocs/build.properties +++ b/services/workdocs/build.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/services/workdocs/pom.xml b/services/workdocs/pom.xml index e4b75cf8d5af..0f9b7247ce62 100644 --- a/services/workdocs/pom.xml +++ b/services/workdocs/pom.xml @@ -1,6 +1,6 @@ + @@ -6,7 +21,7 @@ aws-sdk-java-pom software.amazon.awssdk - 2.10.7-SNAPSHOT + 2.11.8-SNAPSHOT ../../pom.xml @@ -85,6 +100,11 @@ utils ${awsjavasdk.version}
    + + software.amazon.awssdk + profiles + ${awsjavasdk.version} + netty-nio-client software.amazon.awssdk diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/ProfileFileConfigurationTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/ProfileFileConfigurationTest.java new file mode 100644 index 000000000000..a70bb0f2a40f --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/ProfileFileConfigurationTest.java @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute; +import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.core.SdkSystemSetting; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.signer.NoOpSigner; +import software.amazon.awssdk.core.signer.Signer; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; +import software.amazon.awssdk.testutils.EnvironmentVariableHelper; +import software.amazon.awssdk.utils.StringInputStream; + +public class ProfileFileConfigurationTest { + @Test + public void profileIsHonoredForCredentialsAndRegion() { + EnvironmentVariableHelper.run(env -> { + env.remove(SdkSystemSetting.AWS_REGION); + env.remove(SdkSystemSetting.AWS_ACCESS_KEY_ID); + env.remove(SdkSystemSetting.AWS_SECRET_ACCESS_KEY); + + String profileContent = "[profile foo]\n" + + "region = us-banana-46\n" + + "aws_access_key_id = profileIsHonoredForCredentials_akid\n" + + "aws_secret_access_key = profileIsHonoredForCredentials_skid"; + String profileName = "foo"; + Signer signer = mock(NoOpSigner.class); + + ProtocolRestJsonClient client = + ProtocolRestJsonClient.builder() + .overrideConfiguration(overrideConfig(profileContent, profileName, signer)) + .build(); + + Mockito.when(signer.sign(any(), any())).thenCallRealMethod(); + + try { + client.allTypes(); + } catch (SdkClientException e) { + // expected + } + + ArgumentCaptor httpRequest = ArgumentCaptor.forClass(SdkHttpFullRequest.class); + ArgumentCaptor attributes = ArgumentCaptor.forClass(ExecutionAttributes.class); + Mockito.verify(signer).sign(httpRequest.capture(), attributes.capture()); + + AwsCredentials credentials = attributes.getValue().getAttribute(AwsSignerExecutionAttribute.AWS_CREDENTIALS); + assertThat(credentials.accessKeyId()).isEqualTo("profileIsHonoredForCredentials_akid"); + assertThat(credentials.secretAccessKey()).isEqualTo("profileIsHonoredForCredentials_skid"); + + Region region = attributes.getValue().getAttribute(AwsExecutionAttribute.AWS_REGION); + assertThat(region.id()).isEqualTo("us-banana-46"); + + assertThat(httpRequest.getValue().getUri().getHost()).contains("us-banana-46"); + }); + } + + private ClientOverrideConfiguration overrideConfig(String profileContent, String profileName, Signer signer) { + return ClientOverrideConfiguration.builder() + .defaultProfileFile(profileFile(profileContent)) + .defaultProfileName(profileName) + .retryPolicy(r -> r.numRetries(0)) + .putAdvancedOption(SdkAdvancedClientOption.SIGNER, signer) + .build(); + } + + private ProfileFile profileFile(String content) { + return ProfileFile.builder() + .content(new StringInputStream(content)) + .type(ProfileFile.Type.CONFIGURATION) + .build(); + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/GetValueForFieldTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/GetValueForFieldTest.java index 73a311ab5981..17a4f033d290 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/GetValueForFieldTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/GetValueForFieldTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ListCopierTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ListCopierTest.java index dc6a5a69ba6f..0e66060e0824 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ListCopierTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ListCopierTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/MapCopierTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/MapCopierTest.java index 0e0acbefa72e..21b49b13fe87 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/MapCopierTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/MapCopierTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderListMemberTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderListMemberTest.java index bae54df6f057..333f60f71ff5 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderListMemberTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderListMemberTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderMapMemberTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderMapMemberTest.java index febc413ad718..232e1ec3b9b5 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderMapMemberTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/codegenerationjsonrpccustomized/model/ModelBuilderMapMemberTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customresponsemetadata/CustomResponseMetadataTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customresponsemetadata/CustomResponseMetadataTest.java index 85af47742483..e0104a254ff3 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customresponsemetadata/CustomResponseMetadataTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/customresponsemetadata/CustomResponseMetadataTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolquery/AsyncOperationCancelTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolquery/AsyncOperationCancelTest.java index b5cf7c96734c..b6552bb96c9c 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolquery/AsyncOperationCancelTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolquery/AsyncOperationCancelTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/AsyncOperationCancelTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/AsyncOperationCancelTest.java index 096c2853f3e6..5af311ab04eb 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/AsyncOperationCancelTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/AsyncOperationCancelTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/HashCodeEqualsTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/HashCodeEqualsTest.java index 338085fb4455..46da01f4cb14 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/HashCodeEqualsTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestjson/HashCodeEqualsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestxml/AsyncOperationCancelTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestxml/AsyncOperationCancelTest.java index 42edb6ed190b..ce17d2380ba1 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestxml/AsyncOperationCancelTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/protocolrestxml/AsyncOperationCancelTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/AsyncClientRetryModeTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/AsyncClientRetryModeTest.java new file mode 100644 index 000000000000..1151107bb359 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/AsyncClientRetryModeTest.java @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.retry; + +import java.util.concurrent.CompletionException; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClientBuilder; +import software.amazon.awssdk.services.protocolrestjson.model.AllTypesResponse; + +public class AsyncClientRetryModeTest + extends ClientRetryModeTestSuite { + @Override + protected ProtocolRestJsonAsyncClientBuilder newClientBuilder() { + return ProtocolRestJsonAsyncClient.builder(); + } + + @Override + protected AllTypesResponse callAllTypes(ProtocolRestJsonAsyncClient client) { + try { + return client.allTypes().join(); + } catch (CompletionException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + + throw e; + } + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/ClientRetryModeTestSuite.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/ClientRetryModeTestSuite.java new file mode 100644 index 000000000000..35d5d70e23f8 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/ClientRetryModeTestSuite.java @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.retry; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.proxyAllTo; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import java.net.URI; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.junit.Rule; +import org.junit.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.retry.RetryMode; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.protocolrestjson.model.AllTypesResponse; +import software.amazon.awssdk.utils.StringInputStream; + +public abstract class ClientRetryModeTestSuite> { + @Rule + public WireMockRule wireMock = new WireMockRule(0); + + @Test + public void legacyRetryModeIsFourAttempts() { + stubThrottlingResponse(); + ClientT client = clientBuilder().overrideConfiguration(o -> o.retryPolicy(RetryMode.LEGACY)).build(); + assertThatThrownBy(() -> callAllTypes(client)).isInstanceOf(SdkException.class); + verifyRequestCount(4); + } + + @Test + public void standardRetryModeIsThreeAttempts() { + stubThrottlingResponse(); + ClientT client = clientBuilder().overrideConfiguration(o -> o.retryPolicy(RetryMode.STANDARD)).build(); + assertThatThrownBy(() -> callAllTypes(client)).isInstanceOf(SdkException.class); + verifyRequestCount(3); + } + + @Test + public void retryModeCanBeSetByProfileFile() { + ProfileFile profileFile = ProfileFile.builder() + .content(new StringInputStream("[profile foo]\n" + + "retry_mode = standard")) + .type(ProfileFile.Type.CONFIGURATION) + .build(); + stubThrottlingResponse(); + ClientT client = clientBuilder().overrideConfiguration(o -> o.defaultProfileFile(profileFile) + .defaultProfileName("foo")).build(); + assertThatThrownBy(() -> callAllTypes(client)).isInstanceOf(SdkException.class); + verifyRequestCount(3); + } + + @Test + public void legacyRetryModeExcludesThrottlingExceptions() throws InterruptedException { + stubThrottlingResponse(); + + ExecutorService executor = Executors.newFixedThreadPool(51); + ClientT client = clientBuilder().overrideConfiguration(o -> o.retryPolicy(RetryMode.LEGACY)).build(); + + for (int i = 0; i < 51; ++i) { + executor.execute(() -> assertThatThrownBy(() -> callAllTypes(client)).isInstanceOf(SdkException.class)); + } + executor.shutdown(); + assertThat(executor.awaitTermination(30, TimeUnit.SECONDS)).isTrue(); + + // 51 requests * 4 attempts = 204 requests + verifyRequestCount(204); + } + + @Test + public void standardRetryModeIncludesThrottlingExceptions() throws InterruptedException { + stubThrottlingResponse(); + + ExecutorService executor = Executors.newFixedThreadPool(51); + ClientT client = clientBuilder().overrideConfiguration(o -> o.retryPolicy(RetryMode.STANDARD)).build(); + + for (int i = 0; i < 51; ++i) { + executor.execute(() -> assertThatThrownBy(() -> callAllTypes(client)).isInstanceOf(SdkException.class)); + } + executor.shutdown(); + assertThat(executor.awaitTermination(30, TimeUnit.SECONDS)).isTrue(); + + // Would receive 153 without throttling (51 requests * 3 attempts = 153 requests) + verifyRequestCount(151); + } + + private BuilderT clientBuilder() { + return newClientBuilder().credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid"))) + .region(Region.US_EAST_1) + .endpointOverride(URI.create("http://localhost:" + wireMock.port())); + } + + protected abstract BuilderT newClientBuilder(); + + protected abstract AllTypesResponse callAllTypes(ClientT client); + + private void verifyRequestCount(int count) { + verify(count, anyRequestedFor(anyUrl())); + } + + private void stubThrottlingResponse() { + stubFor(post(anyUrl()) + .willReturn(aResponse().withStatus(429))); + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/SyncClientRetryModeTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/SyncClientRetryModeTest.java new file mode 100644 index 000000000000..1d6f4e60adb4 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/retry/SyncClientRetryModeTest.java @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.retry; + +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClientBuilder; +import software.amazon.awssdk.services.protocolrestjson.model.AllTypesResponse; + +public class SyncClientRetryModeTest extends ClientRetryModeTestSuite { + @Override + protected ProtocolRestJsonClientBuilder newClientBuilder() { + return ProtocolRestJsonClient.builder(); + } + + @Override + protected AllTypesResponse callAllTypes(ProtocolRestJsonClient client) { + return client.allTypes(); + } +} diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/tostring/SensitiveDataRedactedTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/tostring/SensitiveDataRedactedTest.java index 5eda86056e17..48b4fc5deba0 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/tostring/SensitiveDataRedactedTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/tostring/SensitiveDataRedactedTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/dynamodbdocument-v1/pom.xml b/test/dynamodbdocument-v1/pom.xml deleted file mode 100644 index fb275f491d05..000000000000 --- a/test/dynamodbdocument-v1/pom.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - 4.0.0 - - aws-sdk-java-pom - software.amazon.awssdk - 2.10.7-SNAPSHOT - ../../pom.xml - - dynamodbdocument-v1 - AWS Java SDK :: Test :: Amazon DynamoDB Document API v1 - DynamoDB Document API largely unchanged from v1. The v1 API is kept for testing purposes only. All classes are in the test directories to prevent use in application code. - https://aws.amazon.com/sdkforjava - - - - - software.amazon.awssdk - bom-internal - ${project.version} - pom - import - - - - - - - software.amazon.awssdk - regions - ${awsjavasdk.version} - test - - - software.amazon.awssdk - annotations - ${awsjavasdk.version} - test - - - software.amazon.awssdk - utils - ${awsjavasdk.version} - test - - - software.amazon.awssdk - sdk-core - ${awsjavasdk.version} - test - - - software.amazon.awssdk - aws-core - ${awsjavasdk.version} - test - - - dynamodb - software.amazon.awssdk - ${awsjavasdk.version} - test - - - s3 - software.amazon.awssdk - ${awsjavasdk.version} - test - - - service-test-utils - software.amazon.awssdk - ${awsjavasdk.version} - test - - - junit - junit - test - - - mockito-core - org.mockito - test - - - diff --git a/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/NestedJsonDocumentIntegrationTest.java b/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/NestedJsonDocumentIntegrationTest.java deleted file mode 100644 index 05846a36a350..000000000000 --- a/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/NestedJsonDocumentIntegrationTest.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.core.util.SdkAutoConstructMap; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.util.TableUtils; -import software.amazon.awssdk.testutils.service.AwsTestBase; - -/** - * DynamoDB supports nested attributes up to 32 levels deep. - * http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html - */ -public class NestedJsonDocumentIntegrationTest extends AwsTestBase { - - private static final String TABLE = "java-sdk-nested-json-document-" + System.currentTimeMillis(); - private static final String HASH = "hash"; - private static final String JSON_MAP_ATTRIBUTE = "json"; - private static final String JSON_MAP_NESTED_KEY = "key"; - /* - * DynamoDB supports nested attributes up to 32 levels deep. - * http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Limits.html - */ - private static final int MAX_JSON_PATH_DEPTH = 32; - private static DynamoDbClient ddb; - - @BeforeClass - public static void setup() throws Exception { - setUpCredentials(); - ddb = DynamoDbClient.builder().credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - - ddb.createTable(CreateTableRequest.builder() - .tableName(TABLE) - .keySchema(KeySchemaElement.builder().attributeName(HASH).keyType(KeyType.HASH).build()) - .attributeDefinitions(AttributeDefinition.builder().attributeName(HASH).attributeType(ScalarAttributeType.S).build()) - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(1L).writeCapacityUnits(1L).build()).build()); - - TableUtils.waitUntilActive(ddb, TABLE); - } - - @AfterClass - public static void tearDown() { - ddb.deleteTable(DeleteTableRequest.builder().tableName(TABLE).build()); - } - - @Test - public void testMaxNestedDepth() { - // minus 1 to account for the top-level attribute - int MAX_MAP_DEPTH = MAX_JSON_PATH_DEPTH - 1; - - AttributeValue nestedJson = buildNestedMapAttribute(MAX_MAP_DEPTH); - - Map item = new HashMap(); - item.put(HASH, AttributeValue.builder().s("foo").build()); - item.put(JSON_MAP_ATTRIBUTE, nestedJson); - - ddb.putItem(PutItemRequest.builder() - .tableName(TABLE) - .item(item) - .build()); - - // Make sure we can read the max-depth item - GetItemResponse itemResult = ddb.getItem(GetItemRequest.builder() - .tableName(TABLE) - .key(Collections.singletonMap(HASH, - AttributeValue.builder().s("foo").build())) - .build()); - int mapDepth = computeDepthOfNestedMapAttribute( - itemResult.item().get(JSON_MAP_ATTRIBUTE)); - Assert.assertEquals(MAX_MAP_DEPTH, mapDepth); - - - // Attempt to put a JSON document with over-limit depth - AttributeValue nestedJson_OverLimit = buildNestedMapAttribute(MAX_MAP_DEPTH + 1); - - Map item_OverLimit = new HashMap(); - item_OverLimit.put(HASH, AttributeValue.builder().s("foo").build()); - item_OverLimit.put("json", nestedJson_OverLimit); - - try { - ddb.putItem(PutItemRequest.builder() - .tableName(TABLE) - .item(item_OverLimit).build()); - Assert.fail("ValidationException is expected, since the depth exceeds the service limit."); - } catch (SdkServiceException expected) { - // Ignored or expected. - } - } - - private AttributeValue buildNestedMapAttribute(int depth) { - AttributeValue value = AttributeValue.builder().s("foo").build(); - while (depth-- > 0) { - value = AttributeValue.builder().m(Collections.singletonMap(JSON_MAP_NESTED_KEY, value)).build(); - } - return value; - } - - private int computeDepthOfNestedMapAttribute(AttributeValue mapAttr) { - int depth = 0; - while (mapAttr != null && mapAttr.m() != null && !(mapAttr.m() instanceof SdkAutoConstructMap)) { - depth++; - mapAttr = mapAttr.m().get(JSON_MAP_NESTED_KEY); - } - return depth; - } -} diff --git a/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/TableUtilsIntegrationTest.java b/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/TableUtilsIntegrationTest.java deleted file mode 100644 index 28a574b32300..000000000000 --- a/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/TableUtilsIntegrationTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; -import software.amazon.awssdk.services.dynamodb.util.TableUtils; -import software.amazon.awssdk.services.dynamodb.util.TableUtils.TableNeverTransitionedToStateException; -import software.amazon.awssdk.testutils.service.AwsIntegrationTestBase; - -public class TableUtilsIntegrationTest extends AwsIntegrationTestBase { - - private static final int CUSTOM_TIMEOUT = 5 * 1000; - - /** - * Wait a generous amount of time after the custom timeout to account for - * variance due to polling interval. This is only used in tests that use - * {@link TableUtilsIntegrationTest#CUSTOM_TIMEOUT} - */ - private static final int TEST_TIMEOUT = CUSTOM_TIMEOUT * 2; - - private static final int CUSTOM_POLLING_INTERVAL = 1 * 1000; - private static final long READ_CAPACITY = 5L; - private static final long WRITE_CAPACITY = 5L; - private static final String HASH_KEY_NAME = "someHash"; - - private static DynamoDbClient ddb; - private String tableName; - - @BeforeClass - public static void setupFixture() { - ddb = DynamoDbClient.builder().credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - } - - private CreateTableRequest createTableRequest() { - return CreateTableRequest.builder() - .tableName(tableName) - .keySchema(KeySchemaElement.builder() - .keyType(KeyType.HASH) - .attributeName(HASH_KEY_NAME).build()) - .attributeDefinitions(AttributeDefinition.builder() - .attributeName(HASH_KEY_NAME) - .attributeType(ScalarAttributeType.S).build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(READ_CAPACITY) - .writeCapacityUnits(WRITE_CAPACITY).build()) - .build(); - } - - private DeleteTableRequest deleteTableRequest() { - return DeleteTableRequest.builder().tableName(tableName).build(); - } - - private void createTable() { - ddb.createTable(createTableRequest()); - } - - @Before - public void setup() { - tableName = "TableUtilsTest-" + System.currentTimeMillis(); - } - - @After - public void tearDown() throws InterruptedException { - if (tableStatus() != null) { - if (!tableStatus().equals(TableStatus.DELETING)) { - TableUtils.waitUntilActive(ddb, tableName); - ddb.deleteTable(DeleteTableRequest.builder().tableName(tableName).build()); - } - waitUntilTableDeleted(); - } - } - - /** - * @return Table status or null if it doesn't exist. - */ - private String tableStatus() { - try { - return ddb.describeTable(DescribeTableRequest.builder().tableName(tableName).build()).table().tableStatusAsString(); - } catch (ResourceNotFoundException e) { - return null; - } - } - - // TODO replace with waiters when available. - private void waitUntilTableDeleted() throws InterruptedException { - long startTime = System.currentTimeMillis(); - // Wait up to five minutes for a table to be deleted. - long endTime = startTime + 5 * 60 * 1000; - while (System.currentTimeMillis() < endTime) { - try { - ddb.describeTable(DescribeTableRequest.builder().tableName(tableName).build()); - Thread.sleep(1000); - } catch (ResourceNotFoundException e) { - return; - } - } - } - - @Test(expected = IllegalArgumentException.class) - public void waitUntilActive_InvalidTimeout_ThrowsException() throws Exception { - TableUtils.waitUntilActive(ddb, tableName, -1, 10); - } - - @Test(expected = IllegalArgumentException.class) - public void waitUntilActive_InvalidInterval_ThrowsException() throws Exception { - TableUtils.waitUntilActive(ddb, tableName, 10, -1); - } - - @Test(expected = IllegalArgumentException.class) - public void waitUntilActive_IntervalGreaterThanTimeout_ThrowsException() throws Exception { - TableUtils.waitUntilActive(ddb, tableName, 10, 100); - } - - @Test - public void waitUntilActive_MethodBlocksUntilTableIsActive() throws Exception { - createTable(); - TableUtils.waitUntilActive(ddb, tableName); - assertEquals(TableStatus.ACTIVE, - ddb.describeTable(DescribeTableRequest.builder().tableName(tableName).build()).table().tableStatus()); - } - - @Test(expected = TableNeverTransitionedToStateException.class, timeout = TEST_TIMEOUT) - public void waitUntilActive_TableNeverTransitionsToActive_ThrowsException() throws Exception { - createTable(); - // We wait long enough for DescribeTable to return something but not - // long enough for the table to transition to active - TableUtils.waitUntilActive(ddb, tableName, 1 * 1000, 500); - } - - @Test(expected = TableNeverTransitionedToStateException.class, timeout = TEST_TIMEOUT) - public void waitUntilActive_NoSuchTable_BlocksUntilTimeoutThenThrowsException() throws - InterruptedException { - TableUtils.waitUntilActive(ddb, tableName, CUSTOM_TIMEOUT, CUSTOM_POLLING_INTERVAL); - } - - @Test - public void waitUntilExists_MethodBlocksUntilTableExists() throws InterruptedException { - createTable(); - TableUtils.waitUntilExists(ddb, tableName); - assertNotNull(ddb.describeTable(DescribeTableRequest.builder().tableName(tableName).build())); - } - - @Test(expected = SdkClientException.class, timeout = TEST_TIMEOUT) - public void waitUntilExists_NoSuchTable_BlocksUntilTimeoutThenThrowsException() throws - InterruptedException { - TableUtils.waitUntilExists(ddb, tableName, CUSTOM_TIMEOUT, CUSTOM_POLLING_INTERVAL); - } - - @Test - public void testCreateTableIfNotExists() throws InterruptedException { - assertTrue(TableUtils.createTableIfNotExists(ddb, createTableRequest())); - TableUtils.waitUntilExists(ddb, tableName); - assertFalse(TableUtils.createTableIfNotExists(ddb, createTableRequest())); - } - - @Test - public void testDeleteTableIfExists() throws InterruptedException { - assertFalse(TableUtils.deleteTableIfExists(ddb, deleteTableRequest())); - createTable(); - TableUtils.waitUntilActive(ddb, tableName); - assertTrue(TableUtils.deleteTableIfExists(ddb, deleteTableRequest())); - waitUntilTableDeleted(); - } - -} diff --git a/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/document/UpdateItemIntegrationTest.java b/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/document/UpdateItemIntegrationTest.java deleted file mode 100644 index 195b905c9a89..000000000000 --- a/test/dynamodbdocument-v1/src/it/java/software/amazon/awssdk/services/dynamodb/document/UpdateItemIntegrationTest.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.spec.GetItemSpec; -import software.amazon.awssdk.services.dynamodb.document.utils.NameMap; -import software.amazon.awssdk.services.dynamodb.document.utils.ValueMap; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.testutils.service.AwsIntegrationTestBase; - -public class UpdateItemIntegrationTest { - - private static final long READ_CAPACITY = 1; - private static final long WRITE_CAPACITY = 1; - private static final Long FIRST_CUSTOMER_ID = 1000L; - private static final String ADDRESS_TYPE_HOME = "home"; - private static final String ADDRESS_TYPE_WORK = "work"; - private static DynamoDb dynamoDb; - private static String TABLE_NAME = "UpdateItemIntegrationTest"; - private static String HASH_KEY = "customer_id"; - private static String RANGE_KEY = "address_type"; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDbClient client = DynamoDbClient.builder() - .credentialsProvider(AwsIntegrationTestBase.CREDENTIALS_PROVIDER_CHAIN) - .build(); - dynamoDb = new DynamoDb(client); - - createTable(); - fillInData(); - } - - private static void createTable() throws Exception { - Table table = dynamoDb.getTable(TABLE_NAME); - TableDescription desc = table.waitForActiveOrDelete(); - if (desc == null) { - // table doesn't exist; let's create it - KeySchemaElement hashKey = - KeySchemaElement.builder().attributeName(HASH_KEY).keyType(KeyType.HASH).build(); - KeySchemaElement rangeKey = - KeySchemaElement.builder().attributeName(RANGE_KEY).keyType(KeyType.RANGE).build(); - CreateTableRequest createTableRequest = CreateTableRequest.builder(). - tableName(TABLE_NAME) - .keySchema(Arrays.asList(hashKey, rangeKey)) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(HASH_KEY).attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName(RANGE_KEY).attributeType(ScalarAttributeType.S).build()) - .provisionedThroughput( - ProvisionedThroughput.builder().readCapacityUnits(READ_CAPACITY).writeCapacityUnits(WRITE_CAPACITY).build()) - .build(); - table = dynamoDb.createTable(createTableRequest); - table.waitForActive(); - } - } - - private static void fillInData() { - Table table = dynamoDb.getTable(TABLE_NAME); - table.putItem(new Item().with(HASH_KEY, FIRST_CUSTOMER_ID) - .with(RANGE_KEY, ADDRESS_TYPE_WORK) - .with("AddressLine1", "1918 8th Aven") - .with("city", "seattle") - .with("state", "WA") - .with("zipcode", 98104)); - table.putItem(new Item().with(HASH_KEY, FIRST_CUSTOMER_ID) - .with(RANGE_KEY, ADDRESS_TYPE_HOME) - .with("AddressLine1", "15606 NE 40th ST") - .with("city", "redmond") - .with("state", "WA") - .with("zipcode", 98052)); - } - - @AfterClass - public static void shutDown() throws Exception { - // Table table = dynamoDB.getTable(TABLE_NAME); - // table.delete(); - dynamoDb.shutdown(); - } - - /** - * This test case tests the various methods in AttributeUpdate class. At - * each phase, retrieves the items and compares its values. - */ - @Test - public void testAddingNewAttributeToExistingRow() { - final String phoneNumber1 = "123-456-7890"; - final Set phoneNumbers = new HashSet(); - phoneNumbers.add(phoneNumber1); - - // Adds a new attribute to the row. - Table table = dynamoDb.getTable(TABLE_NAME); - table.updateItem(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK, - new AttributeUpdate("phone").put(phoneNumbers)); - Item item = table.getItem(new GetItemSpec() - .withPrimaryKey(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true) - ); - Set phoneNumbersRetrieved = item.getStringSet("phone"); - assertEquals(phoneNumbers, phoneNumbersRetrieved); - assertTrue(phoneNumbersRetrieved.contains(phoneNumber1)); - assertTrue(phoneNumbersRetrieved.size() == 1); - - // Adds a new element to the attribute - final String phoneNumber2 = "987-654-3210"; - table.updateItem(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK, - new AttributeUpdate("phone").addElements(phoneNumber2)); - item = table.getItem(new GetItemSpec() - .withPrimaryKey(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true)); - phoneNumbersRetrieved = item.getStringSet("phone"); - assertTrue(phoneNumbersRetrieved.contains(phoneNumber2)); - assertTrue(phoneNumbersRetrieved.contains(phoneNumber1)); - assertTrue(phoneNumbersRetrieved.size() == 2); - - // removes an element from the attribute - table.updateItem(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK, - new AttributeUpdate("phone").removeElements(phoneNumber2)); - item = table.getItem(new GetItemSpec() - .withPrimaryKey(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true)); - phoneNumbersRetrieved = item.getStringSet("phone"); - assertFalse(phoneNumbersRetrieved.contains(phoneNumber2)); - assertTrue(phoneNumbersRetrieved.contains(phoneNumber1)); - assertTrue(phoneNumbersRetrieved.size() == 1); - - // deletes the attribute - table.updateItem(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK, new AttributeUpdate("phone").delete()); - item = table.getItem(new GetItemSpec() - .withPrimaryKey(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true)); - phoneNumbersRetrieved = item.getStringSet("phone"); - assertNull(phoneNumbersRetrieved); - - final Number oldValue = item.getNumber("zipcode"); - - // Increments the zip code attribute - table.updateItem(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK, - new AttributeUpdate("zipcode").addNumeric(1)); - item = table.getItem(new GetItemSpec() - .withPrimaryKey(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true)); - Number newValue = item.getNumber("zipcode"); - assertEquals(oldValue.longValue() + 1, newValue.longValue()); - - // Decrements the zip code attribute - table.updateItem(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK, - new AttributeUpdate("zipcode").addNumeric(-1)); - item = table.getItem(new GetItemSpec() - .withPrimaryKey(HASH_KEY, FIRST_CUSTOMER_ID, RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true)); - newValue = item.getNumber("zipcode"); - assertEquals(oldValue.longValue(), newValue.longValue()); - } - - /** - * This test cases performs an update item with expected set. The update - * must fail as the expected condition is not met. - */ - @Test - public void testUpdateItemWithExpectedSet() { - final String phoneNumber1 = "123-456-7890"; - final String phoneNumber2 = "987-654-3210"; - final Set phoneNumbers = new HashSet(); - phoneNumbers.add(phoneNumber1); - Table table = dynamoDb.getTable(TABLE_NAME); - try { - table.updateItem( - HASH_KEY, FIRST_CUSTOMER_ID, - RANGE_KEY, ADDRESS_TYPE_WORK, - Arrays.asList(new Expected("phone").eq(phoneNumbers)), - new AttributeUpdate("phone").addElements(phoneNumber2)); - fail("Update Should fail as the phone number attribute is not present in the row"); - } catch (Exception e) { - assertTrue(e instanceof SdkServiceException); - } - } - - /** - * Performs an update using the update expression. Asserts by retrieving the - * item and checking if the update values are present in the record. - */ - @Test - public void testUpdateItemWithUpdateExpression() { - final String phoneNumber1 = "123-456-7890"; - final String phoneNumber2 = "987-654-3210"; - final Set phoneNumbers = new HashSet(); - phoneNumbers.add(phoneNumber1); - phoneNumbers.add(phoneNumber2); - final String updateExpression = "set #phoneAttributeName = :phoneAtributeValue"; - - final Map nameMap = new HashMap(); - nameMap.put("#phoneAttributeName", "phone"); - final Map valueMap = new HashMap(); - valueMap.put(":phoneAtributeValue", phoneNumbers); - - Table table = dynamoDb.getTable(TABLE_NAME); - table.updateItem( - HASH_KEY, FIRST_CUSTOMER_ID, - RANGE_KEY, ADDRESS_TYPE_WORK, - updateExpression, nameMap, valueMap); - Item item = table.getItem(new GetItemSpec() - .withPrimaryKey( - HASH_KEY, FIRST_CUSTOMER_ID, - RANGE_KEY, ADDRESS_TYPE_WORK) - .withConsistentRead(true)); - Set phoneNumbersRetrieved = item.getStringSet("phone"); - assertNotNull(phoneNumbersRetrieved); - assertTrue(phoneNumbersRetrieved.size() == 2); - assertTrue(phoneNumbersRetrieved.contains(phoneNumber1)); - assertTrue(phoneNumbersRetrieved.contains(phoneNumber2)); - } - - /** - * Performs an update using the update and conditional expression. The - * update should fail as the conditional expression fails to true. - */ - @Test - public void testUpdateItemWithConditionExpression() { - Table table = dynamoDb.getTable(TABLE_NAME); - try { - table.updateItem( - HASH_KEY, FIRST_CUSTOMER_ID, - RANGE_KEY, ADDRESS_TYPE_WORK, - "set #mno = list_append(:phoneNumber, :phoneNumber)", - "zipcode = :zipcode", - new NameMap().with("#mno", "phone"), - new ValueMap() - .withList(":phoneNumber", "987-654-3210") - // compare zipecode, which is of type int, to string - // leading to an intentional failure in the update condition - .withString(":zipcode", "98104") - ); - fail("Update Should fail as the zip code mentioned in the condition expression doesn't match"); - } catch (SdkServiceException e) { - assertTrue(e.getMessage().contains("conditional request failed")); - } - } - -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Attribute.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Attribute.java deleted file mode 100644 index 43c0678426ff..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Attribute.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - -/** - * A key/value pair. - */ -public class Attribute { - private final String name; - private final Object value; - - public Attribute(String attrName, Object value) { - InternalUtils.checkInvalidAttrName(attrName); - this.name = attrName; - this.value = value; - } - - public String name() { - return name; - } - - public Object value() { - return value; - } - - @Override - public String toString() { - return "{" + name + ": " + value + "}"; - } - - @Override - public int hashCode() { - final int prime = 31; - int hashCode = 1; - // attribute name is never null as enforced in ctor - hashCode = prime * hashCode + name().hashCode(); - hashCode = prime * hashCode - + ((value() == null) ? 0 : value().hashCode()); - return hashCode; - } - - @Override - public boolean equals(Object in) { - if (in instanceof Attribute) { - Attribute that = (Attribute) in; - if (this.name.equals(that.name)) { - if (this.value == null) { - return that.value == null; - } else { - return this.value.equals(that.value); - } - } - } - return false; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/AttributeTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/AttributeTest.java deleted file mode 100644 index 1de470ca0d02..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/AttributeTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.HashSet; -import java.util.Set; -import org.junit.Test; - -public class AttributeTest { - - @Test(expected = IllegalArgumentException.class) - public void nullAttributeName() { - new Attribute(null, "invalid attribute name"); - } - - @Test - public void nullAttributeValue() { - Attribute a = new Attribute("null attribute value is fine", null); - assertTrue(a.hashCode() != 0); - } - - @Test - public void testHashCode() { - Attribute a1 = new Attribute("name", null); - Attribute a2 = new Attribute("name", "a2"); - Attribute a3 = new Attribute("name", "a3"); - Attribute a4 = new Attribute("name4", "a3"); - Set checkUniqueness = new HashSet(); - checkUniqueness.add(a1.hashCode()); - checkUniqueness.add(a2.hashCode()); - checkUniqueness.add(a3.hashCode()); - checkUniqueness.add(a4.hashCode()); - assertTrue(checkUniqueness.size() == 4); - } - - @Test - public void testEquals() { - Attribute a1 = new Attribute("name", null); - Attribute a2 = new Attribute("name", "a2"); - Attribute a3 = new Attribute("name", "a3"); - Attribute a4 = new Attribute("name4", "a3"); - Set checkUniqueness = new HashSet(); - checkUniqueness.add(a1); - checkUniqueness.add(a2); - checkUniqueness.add(a3); - checkUniqueness.add(a4); - assertTrue(checkUniqueness.size() == 4); - - assertTrue(checkUniqueness.contains(new Attribute("name", null))); - assertTrue(checkUniqueness.contains(new Attribute("name", "a2"))); - assertTrue(checkUniqueness.contains(new Attribute("name", "a3"))); - assertTrue(checkUniqueness.contains(new Attribute("name4", "a3"))); - - assertFalse(checkUniqueness.contains(new Attribute("not", "exist"))); - assertFalse(a1.equals("name")); - assertFalse(a1.equals(null)); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/AttributeUpdate.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/AttributeUpdate.java deleted file mode 100644 index b50afc0da81c..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/AttributeUpdate.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeAction; - -/** - * Used to update an attribute. Each instance of AttributeUpdate includes the - * name, action and new value to be used for modifying the attribute. - *

    - * Typical usages: - *

    - * new AttributeUpdate("strAttr").put("attrValue"); - *

    - * new AttributeUpdate("intAttr").addNumeric(42); - *

    - * ... - *

    - *

    - * See http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/ - * API_UpdateItem.html#DDB-UpdateItem-request-AttributeUpdates - */ -public class AttributeUpdate { - - private final String attributeName; - - private AttributeAction action; - - private Set attributeValues; - - private Object value; - - /** - * Used to update an attribute. Each instance of AttributeUpdate includes the - * name, action and new value to be used for modifying the attribute. - *

    - * Typical usages: - *

    - * new AttributeUpdate("strAttr").put("attrValue"); - *

    - * new AttributeUpdate("intAttr").addNumeric(42); - *

    - * ... - *

    - *

    - * See http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/ - * API_UpdateItem.html#DDB-UpdateItem-request-AttributeUpdates - */ - public AttributeUpdate(String attributeName) { - this.attributeName = attributeName; - } - - /** - * Used to update an attribute. Each instance of AttributeUpdate includes the - * name, action and new value to be used for modifying the attribute. - *

    - * Typical usages: - *

    - * new AttributeUpdate("strAttr").put("attrValue"); - *

    - * new AttributeUpdate("intAttr").addNumeric(42); - *

    - * ... - *

    - *

    - * See http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/ - * API_UpdateItem.html#DDB-UpdateItem-request-AttributeUpdates - */ - public AttributeUpdate put(Object attributeValue) { - action = AttributeAction.PUT; - this.value = attributeValue; - return this; - } - - public AttributeUpdate delete() { - action = AttributeAction.DELETE; - return this; - } - - public AttributeUpdate removeElements(Object... elementsToBeRemoved) { - action = AttributeAction.DELETE; - this.attributeValues = Collections.unmodifiableSet(new LinkedHashSet( - Arrays.asList(elementsToBeRemoved))); - return this; - } - - public AttributeUpdate addNumeric(Number value) { - action = AttributeAction.ADD; - this.value = value; - return this; - } - - public AttributeUpdate addElements(Object... newElements) { - action = AttributeAction.ADD; - this.attributeValues = Collections.unmodifiableSet(new LinkedHashSet( - Arrays.asList(newElements))); - return this; - } - - public String getAttributeName() { - return attributeName; - } - - public AttributeAction getAction() { - return action; - } - - public Set getAttributeValues() { - return attributeValues; - } - - public Object value() { - return value; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/BatchGetItemOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/BatchGetItemOutcome.java deleted file mode 100644 index 3e141fc9fb8d..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/BatchGetItemOutcome.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.api.BatchGetItemApi; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchGetItemSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; - -/** - * The outcome of a batch get-item operation from DynamoDB. - */ -public class BatchGetItemOutcome { - private final BatchGetItemResponse result; - - /** - * @param result the low-level result; must not be null - */ - public BatchGetItemOutcome(BatchGetItemResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns a map of table name to the list of retrieved items - */ - public Map> getTableItems() { - Map>> res = - result.responses(); - Map> map = new LinkedHashMap>(res.size()); - for (Map.Entry>> e - : res.entrySet()) { - String tableName = e.getKey(); - List> items = e.getValue(); - map.put(tableName, InternalUtils.toItemList(items)); - } - return map; - } - - /** - * Convenient method to return the low-level unprocessed keys. - * - * @see BatchGetItemApi#batchGetItemUnprocessed(Map) - * @see BatchGetItemApi#batchGetItemUnprocessed(software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity, - * Map) - * @see BatchGetItemSpec#withUnprocessedKeys(Map) - */ - public Map getUnprocessedKeys() { - return result.unprocessedKeys(); - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public BatchGetItemResponse batchGetItemResponse() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/BatchWriteItemOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/BatchWriteItemOutcome.java deleted file mode 100644 index 64749efaef06..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/BatchWriteItemOutcome.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.api.BatchWriteItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchWriteItemSpec; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemResponse; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * The outcome of a batch write-item operation from DynamoDB. - */ -public class BatchWriteItemOutcome { - private final BatchWriteItemResponse result; - - /** - * @param result the low-level result; must not be null - */ - public BatchWriteItemOutcome(BatchWriteItemResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Convenient method to return the low-level unprocessed items. - * - * @see BatchWriteItemApi#batchWriteItemUnprocessed(Map) - * @see BatchWriteItemSpec#withUnprocessedItems(Map) - */ - public Map> getUnprocessedItems() { - return result.unprocessedItems(); - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public BatchWriteItemResponse batchWriteItemResult() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/DeleteItemOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/DeleteItemOutcome.java deleted file mode 100644 index d726ee198238..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/DeleteItemOutcome.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemResponse; - -/** - * The outcome of deleting an item from a DynamoDB table. - */ -public class DeleteItemOutcome { - private final DeleteItemResponse result; - - /** - * @param result the low-level result; must not be null - */ - public DeleteItemOutcome(DeleteItemResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns all the returned attributes as a (non-null) {@link Item}. - */ - public Item getItem() { - Map attributes = - InternalUtils.toSimpleMapValue(result.attributes()); - Item item = Item.fromMap(attributes); - return item; - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public DeleteItemResponse getDeleteItemResponse() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/DynamoDb.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/DynamoDb.java deleted file mode 100644 index eae87598278e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/DynamoDb.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.api.BatchGetItemApi; -import software.amazon.awssdk.services.dynamodb.document.api.BatchWriteItemApi; -import software.amazon.awssdk.services.dynamodb.document.api.ListTablesApi; -import software.amazon.awssdk.services.dynamodb.document.internal.BatchGetItemImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.BatchWriteItemImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.ListTablesImpl; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchGetItemSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchWriteItemSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.ListTablesSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * DynamoDB Document API. This class is the entry point to make use of this - * library. - */ -@ThreadSafe -public class DynamoDb implements ListTablesApi, BatchGetItemApi, - BatchWriteItemApi { - private final DynamoDbClient client; - - private final ListTablesImpl listTablesDelegate; - private final BatchGetItemImpl batchGetItemDelegate; - private final BatchWriteItemImpl batchWriteItemDelegate; - - public DynamoDb(DynamoDbClient client) { - if (client == null) { - throw new IllegalArgumentException(); - } - this.client = client; - this.listTablesDelegate = new ListTablesImpl(client); - this.batchGetItemDelegate = new BatchGetItemImpl(client); - this.batchWriteItemDelegate = new BatchWriteItemImpl(client); - } - - /** - * Create a DynamoDB object that talks to the specified AWS region. The - * underlying service client will use all the default client configurations, - * including the default credentials provider chain. See - * {@link DynamoDbClient#DynamoDbClient()} for more information. - *

    BatchWriteRetryStrategyTest - * If you need more control over the client configuration, use - * {@link DynamoDb#DynamoDb(DynamoDbClient)} instead. - * - * @param regionEnum - * the AWS region enum - * @see DynamoDbClient#DynamoDbClient() - */ - public DynamoDb(Region regionEnum) { - this(DynamoDbClient.builder().region(regionEnum).build()); - } - - /** - * Returns the specified DynamoDB table. No network call is involved. - */ - public Table getTable(String tableName) { - return new Table(client, tableName); - } - - /** - * Creates the specified table in DynamoDB. - */ - public Table createTable(CreateTableRequest req) { - CreateTableResponse result = client.createTable(req); - return new Table(client, req.tableName(), - result.tableDescription()); - } - - /** - * Creates the specified table in DynamoDB. - */ - public Table createTable(String tableName, - List keySchema, - List attributeDefinitions, - ProvisionedThroughput provisionedThroughput) { - return createTable(CreateTableRequest.builder() - .tableName(tableName) - .keySchema(keySchema) - .attributeDefinitions(attributeDefinitions) - .provisionedThroughput(provisionedThroughput) - .build()); - } - - @Override - public TableCollection listTables() { - return listTablesDelegate.listTables(); - } - - @Override - public TableCollection listTables(String exclusiveStartTableName) { - return listTablesDelegate.listTables(exclusiveStartTableName); - } - - @Override - public TableCollection listTables(String exclusiveStartTableName, - int maxResultSize) { - return listTablesDelegate.listTables(exclusiveStartTableName, - maxResultSize); - } - - @Override - public TableCollection listTables(int maxResultSize) { - return listTablesDelegate.listTables(maxResultSize); - } - - @Override - public TableCollection listTables(ListTablesSpec spec) { - return listTablesDelegate.listTables(spec); - } - - @Override - public BatchGetItemOutcome batchGetItem( - ReturnConsumedCapacity returnConsumedCapacity, - TableKeysAndAttributes... tableKeysAndAttributes) { - return batchGetItemDelegate.batchGetItem(returnConsumedCapacity, - tableKeysAndAttributes); - } - - @Override - public BatchGetItemOutcome batchGetItem( - TableKeysAndAttributes... tableKeysAndAttributes) { - return batchGetItemDelegate.batchGetItem(tableKeysAndAttributes); - } - - @Override - public BatchGetItemOutcome batchGetItem(BatchGetItemSpec spec) { - return batchGetItemDelegate.batchGetItem(spec); - } - - @Override - public BatchGetItemOutcome batchGetItemUnprocessed( - ReturnConsumedCapacity returnConsumedCapacity, - Map unprocessedKeys) { - return batchGetItemDelegate.batchGetItemUnprocessed( - returnConsumedCapacity, unprocessedKeys); - } - - @Override - public BatchGetItemOutcome batchGetItemUnprocessed( - Map unprocessedKeys) { - return batchGetItemDelegate.batchGetItemUnprocessed(unprocessedKeys); - } - - @Override - public BatchWriteItemOutcome batchWriteItem( - TableWriteItems... tableWriteItems) { - return batchWriteItemDelegate.batchWriteItem(tableWriteItems); - } - - @Override - public BatchWriteItemOutcome batchWriteItem(BatchWriteItemSpec spec) { - return batchWriteItemDelegate.batchWriteItem(spec); - } - - @Override - public BatchWriteItemOutcome batchWriteItemUnprocessed( - Map> unprocessedItems) { - return batchWriteItemDelegate.batchWriteItemUnprocessed(unprocessedItems); - } - - /** - * Shuts down and release all resources. - */ - public void shutdown() throws Exception { - client.close(); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Expected.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Expected.java deleted file mode 100644 index f1d0e718c563..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Expected.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; - - -/** - * Represents a condition to be compared with an attribute value. - *

    - * Typical usages: - *

    - * new Expected("strAttr").eq("attrValue"); - *

    - * new Expected("intAttr").gt(42); - *

    - * ... - *

    - *

    - * See - * http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/ - * API_ExpectedAttributeValue.html. - */ -public class Expected { - private final String attribute; - private ComparisonOperator op; - private Object[] values; - - public Expected(String attrName) { - InternalUtils.checkInvalidAttrName(attrName); - this.attribute = attrName; - } - - /** Returns the attribute. */ - public String getAttribute() { - return attribute; - } - - public ComparisonOperator getComparisonOperator() { - return op; - } - - public Object[] values() { - return values == null ? null : values.clone(); - } - - private Expected values(Object... values) { - this.values = values.clone(); - return this; - } - - private Expected withComparisonOperator(ComparisonOperator op) { - this.op = op; - return this; - } - - /** - * Creates and returns a condition of the range key being equal to the given - * value. - */ - public Expected eq(Object val) { - return withComparisonOperator(ComparisonOperator.EQ).values(val); - } - - public Expected ne(Object val) { - return withComparisonOperator(ComparisonOperator.NE).values(val); - } - - /** - * Expects the attribute be an existing attribute. - */ - public Expected exists() { - return withComparisonOperator(ComparisonOperator.NOT_NULL); - } - - /** - * Expects the attribute be non-existing. - */ - public Expected notExist() { - return withComparisonOperator(ComparisonOperator.NULL); - } - - public Expected contains(Object val) { - return withComparisonOperator(ComparisonOperator.CONTAINS).values(val); - } - - public Expected notContains(Object val) { - return withComparisonOperator(ComparisonOperator.NOT_CONTAINS).values(val); - } - - /** - * Creates and returns a condition of the range key with a value that begins - * with the given value. - */ - public Expected beginsWith(String val) { - return withComparisonOperator(ComparisonOperator.BEGINS_WITH).values(val); - } - - public Expected in(Object... values) { - if (values == null || values.length == 0) { - throw new IllegalArgumentException("values must not be null or empty."); - } - - return withComparisonOperator(ComparisonOperator.IN).values(values); - } - - /** - * Creates and returns a condition of the range key that has a value between - * the given values. - */ - public Expected between(Object low, Object hi) { - return withComparisonOperator(ComparisonOperator.BETWEEN).values(low, hi); - } - - /** - * Creates and returns a condition of the range key being greater than or - * equal to the given value. - */ - public Expected ge(Object val) { - return withComparisonOperator(ComparisonOperator.GE).values(val); - } - - /** - * Creates and returns a condition of the range key being greater than the - * given value. - */ - public Expected gt(Object val) { - return withComparisonOperator(ComparisonOperator.GT).values(val); - } - - /** - * Creates and returns a condition of the range key being less than or equal - * to the given value. - */ - public Expected le(Object val) { - return withComparisonOperator(ComparisonOperator.LE).values(val); - } - - /** - * Creates and returns a condition of the range key being less than the - * given value. - */ - public Expected lt(Object val) { - return withComparisonOperator(ComparisonOperator.LT).values(val); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ExpectedTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ExpectedTest.java deleted file mode 100644 index 83e8625deea6..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ExpectedTest.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.core.util.SdkAutoConstructList; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -public class ExpectedTest { - - private static Entry toExpectedAttributeValue(Expected expected) { - Map map = InternalUtils - .toExpectedAttributeValueMap(Arrays.asList(expected)); - Assert.assertEquals(1, map.size()); - - Iterator> iter = map.entrySet().iterator(); - return iter.next(); - } - - @Test - public void testExpected_EQ() { - Expected expected = new Expected("foo").eq("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.EQ, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - - expected = new Expected("foo").eq(null); - ddbExpected = toExpectedAttributeValue(expected); - ddbExpected_attrName = ddbExpected.getKey(); - ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.EQ, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals(true, ddbExpected_value.attributeValueList().get(0).nul()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_NE() { - Expected expected = new Expected("foo").ne("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.NE, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_EXISTS() { - Expected expected = new Expected("foo").exists(); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.NOT_NULL, ddbExpected_value.comparisonOperator()); - Assert.assertTrue(ddbExpected_value.attributeValueList() instanceof SdkAutoConstructList); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_NOTEXISTS() { - Expected expected = new Expected("foo").notExist(); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.NULL, ddbExpected_value.comparisonOperator()); - Assert.assertTrue(ddbExpected_value.attributeValueList() instanceof SdkAutoConstructList); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_CONTAINS() { - Expected expected = new Expected("foo").contains("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.CONTAINS, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_NOTCONTAINS() { - Expected expected = new Expected("foo").notContains("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.NOT_CONTAINS, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_BEGINSWITH() { - Expected expected = new Expected("foo").beginsWith("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.BEGINS_WITH, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_IN() { - // Single value - Expected expected = new Expected("foo").in("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.IN, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - - // Multi-value - expected = new Expected("foo").in("bar", "charlie", null); - ddbExpected = toExpectedAttributeValue(expected); - ddbExpected_attrName = ddbExpected.getKey(); - ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(3, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals("charlie", ddbExpected_value.attributeValueList().get(1).s()); - Assert.assertEquals(true, ddbExpected_value.attributeValueList().get(2).nul()); - Assert.assertEquals(ComparisonOperator.IN, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - - // Null values - try { - expected = new Expected("foo").in((Object[]) null); - Assert.fail(); - } catch (IllegalArgumentException e) { - // Ignored or expected. - } - - // Empty values - try { - expected = new Expected("foo").in(); - Assert.fail(); - } catch (IllegalArgumentException e) { - // Ignored or expected. - } - } - - @Test - public void testExpected_BETWEEN() { - Expected expected = new Expected("foo").between(0, 100); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(2, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("0", ddbExpected_value.attributeValueList().get(0).n()); - Assert.assertEquals("100", ddbExpected_value.attributeValueList().get(1).n()); - Assert.assertEquals(ComparisonOperator.BETWEEN, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_GE() { - Expected expected = new Expected("foo").ge("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.GE, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_GT() { - Expected expected = new Expected("foo").gt("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.GT, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_LE() { - Expected expected = new Expected("foo").le("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.LE, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_LT() { - Expected expected = new Expected("foo").lt("bar"); - Entry ddbExpected = toExpectedAttributeValue(expected); - String ddbExpected_attrName = ddbExpected.getKey(); - ExpectedAttributeValue ddbExpected_value = ddbExpected.getValue(); - - Assert.assertEquals("foo", ddbExpected_attrName); - Assert.assertEquals(ComparisonOperator.LT, ddbExpected_value.comparisonOperator()); - Assert.assertEquals(1, ddbExpected_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbExpected_value.attributeValueList().get(0).s()); - Assert.assertEquals(null, ddbExpected_value.value()); - Assert.assertEquals(null, ddbExpected_value.exists()); - } - - @Test - public void testExpected_EmptyAttributeName() { - try { - new Expected(null); - Assert.fail(); - } catch (IllegalArgumentException expected) { - // Ignored or expected. - } - - try { - new Expected(""); - Assert.fail(); - } catch (IllegalArgumentException expected) { - // Ignored or expected. - } - } - - @Test - public void testExpected_DuplicateAttribute() { - Table fakeTable = new Table(DynamoDbClient.builder().region(Region.US_WEST_2).build(), "fake-table"); - try { - fakeTable.putItem(new Item(), - new Expected("foo").eq("bar"), - new Expected("foo").eq("charlie")); - Assert.fail(); - } catch (IllegalArgumentException expected) { - // Ignored or expected. - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/FilterConditionTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/FilterConditionTest.java deleted file mode 100644 index 42f8c4e7a822..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/FilterConditionTest.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.core.util.SdkAutoConstructList; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; - -/** - * Covers ScanFilter, which shares the same underlying implementation as QueryFilter. - */ -public class FilterConditionTest { - - private static Entry toAttributeCondition(ScanFilter ScanFilter) { - Map map = InternalUtils - .toAttributeConditionMap(Arrays.asList(ScanFilter)); - Assert.assertEquals(1, map.size()); - - Iterator> iter = map.entrySet().iterator(); - return iter.next(); - } - - @Test - public void testScanFilter_EQ() { - ScanFilter ScanFilter = new ScanFilter("foo").eq("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.EQ, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - - ScanFilter = new ScanFilter("foo").eq(null); - ddbscanFilter = toAttributeCondition(ScanFilter); - ddbscanFilter_attrName = ddbscanFilter.getKey(); - ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.EQ, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals(true, ddbscanFilter_value.attributeValueList().get(0).nul()); - } - - @Test - public void testScanFilter_NE() { - ScanFilter ScanFilter = new ScanFilter("foo").ne("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.NE, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_EXISTS() { - ScanFilter ScanFilter = new ScanFilter("foo").exists(); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.NOT_NULL, ddbscanFilter_value.comparisonOperator()); - Assert.assertTrue(ddbscanFilter_value.attributeValueList() instanceof SdkAutoConstructList); - } - - @Test - public void testScanFilter_NOTEXISTS() { - ScanFilter ScanFilter = new ScanFilter("foo").notExist(); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.NULL, ddbscanFilter_value.comparisonOperator()); - Assert.assertTrue(ddbscanFilter_value.attributeValueList() instanceof SdkAutoConstructList); - } - - @Test - public void testScanFilter_CONTAINS() { - ScanFilter ScanFilter = new ScanFilter("foo").contains("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.CONTAINS, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_NOTCONTAINS() { - ScanFilter ScanFilter = new ScanFilter("foo").notContains("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.NOT_CONTAINS, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_BEGINSWITH() { - ScanFilter ScanFilter = new ScanFilter("foo").beginsWith("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.BEGINS_WITH, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_IN() { - // Single value - ScanFilter ScanFilter = new ScanFilter("foo").in("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.IN, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - - // Multi-value - ScanFilter = new ScanFilter("foo").in("bar", "charlie", null); - ddbscanFilter = toAttributeCondition(ScanFilter); - ddbscanFilter_attrName = ddbscanFilter.getKey(); - ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(3, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - Assert.assertEquals("charlie", ddbscanFilter_value.attributeValueList().get(1).s()); - Assert.assertEquals(true, ddbscanFilter_value.attributeValueList().get(2).nul()); - Assert.assertEquals(ComparisonOperator.IN, ddbscanFilter_value.comparisonOperator()); - - // Null values - try { - ScanFilter = new ScanFilter("foo").in((Object[]) null); - Assert.fail(); - } catch (IllegalArgumentException e) { - // Ignored or expected. - } - - // Empty values - try { - ScanFilter = new ScanFilter("foo").in(); - Assert.fail(); - } catch (IllegalArgumentException e) { - // Ignored or expected. - } - } - - @Test - public void testScanFilter_BETWEEN() { - ScanFilter ScanFilter = new ScanFilter("foo").between(0, 100); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(2, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("0", ddbscanFilter_value.attributeValueList().get(0).n()); - Assert.assertEquals("100", ddbscanFilter_value.attributeValueList().get(1).n()); - Assert.assertEquals(ComparisonOperator.BETWEEN, ddbscanFilter_value.comparisonOperator()); - } - - @Test - public void testScanFilter_GE() { - ScanFilter ScanFilter = new ScanFilter("foo").ge("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.GE, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_GT() { - ScanFilter ScanFilter = new ScanFilter("foo").gt("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.GT, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_LE() { - ScanFilter ScanFilter = new ScanFilter("foo").le("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.LE, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_LT() { - ScanFilter ScanFilter = new ScanFilter("foo").lt("bar"); - Entry ddbscanFilter = toAttributeCondition(ScanFilter); - String ddbscanFilter_attrName = ddbscanFilter.getKey(); - Condition ddbscanFilter_value = ddbscanFilter.getValue(); - - Assert.assertEquals("foo", ddbscanFilter_attrName); - Assert.assertEquals(ComparisonOperator.LT, ddbscanFilter_value.comparisonOperator()); - Assert.assertEquals(1, ddbscanFilter_value.attributeValueList().size()); - Assert.assertEquals("bar", ddbscanFilter_value.attributeValueList().get(0).s()); - } - - @Test - public void testScanFilter_EmptyAttributeName() { - try { - new ScanFilter(null); - Assert.fail(); - } catch (IllegalArgumentException ScanFilter) { - // Ignored or expected. - } - - try { - new ScanFilter(""); - Assert.fail(); - } catch (IllegalArgumentException ScanFilter) { - // Ignored or expected. - } - } - - @Test - public void testScanFilter_DuplicateAttribute() { - Table fakeTable = new Table(DynamoDbClient.builder().region(Region.US_WEST_2).build(), "fake-table"); - try { - fakeTable.scan( - new ScanFilter("foo").eq("bar"), - new ScanFilter("foo").eq("charlie")); - Assert.fail(); - } catch (IllegalArgumentException ScanFilter) { - // Ignored or expected. - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/GetItemOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/GetItemOutcome.java deleted file mode 100644 index 8f542961c5de..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/GetItemOutcome.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; - -/** - * The outcome of getting an item from DynamoDB table. - */ -public class GetItemOutcome { - private final GetItemResponse result; - - /** - * @param result the low-level result; must not be null - */ - public GetItemOutcome(GetItemResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns all the returned attributes as an {@link Item}; or null if the - * item doesn't exist. - */ - public Item getItem() { - Map attributes = - InternalUtils.toSimpleMapValue(result.item()); - Item item = Item.fromMap(attributes); - return item; - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public GetItemResponse getGetItemResponse() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/IncompatibleTypeException.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/IncompatibleTypeException.java deleted file mode 100644 index 3bb5918b3d55..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/IncompatibleTypeException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.core.exception.SdkClientException; - -/** - * Thrown upon incompatible type during data conversion. - */ -public class IncompatibleTypeException extends SdkClientException { - private static final long serialVersionUID = 1L; - - public IncompatibleTypeException(String message) { - super(SdkClientException.builder().message(message)); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Index.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Index.java deleted file mode 100644 index dee44e58f814..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Index.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.api.QueryApi; -import software.amazon.awssdk.services.dynamodb.document.api.ScanApi; -import software.amazon.awssdk.services.dynamodb.document.internal.IndexQueryImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.IndexScanImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.ScanImpl; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.UpdateTableSpec; -import software.amazon.awssdk.services.dynamodb.model.DeleteGlobalSecondaryIndexAction; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexDescription; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexUpdate; -import software.amazon.awssdk.services.dynamodb.model.IndexStatus; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.services.dynamodb.model.UpdateGlobalSecondaryIndexAction; - -/** - * Represents a secondary index on a DynamoDB table. This covers - * both GSI (Global Secondary Index) and LSI (Local Secondary Index). Instance - * of this class can be obtained via {@link Table#getIndex(String)}. - */ -@ThreadSafe -public class Index implements QueryApi, ScanApi { - private static final long SLEEP_TIME_MILLIS = 5000; - private final Table table; - private final String indexName; - private final QueryApi queryDelegate; - private final ScanImpl scanDelegate; - - Index(DynamoDbClient client, String indexName, Table table) { - if (client == null) { - throw new IllegalArgumentException("client must be specified"); - } - if (indexName == null || indexName.trim().length() == 0) { - throw new IllegalArgumentException("index name must not be null or empty"); - } - if (table == null) { - throw new IllegalArgumentException("table must be specified"); - } - this.table = table; - this.indexName = indexName; - this.queryDelegate = new IndexQueryImpl(client, this); - this.scanDelegate = new IndexScanImpl(client, this); - } - - /** - * Returns the owning table. - */ - public final Table getTable() { - return table; - } - - /** - * @return the name of this index - */ - public final String getIndexName() { - return indexName; - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition) { - return queryDelegate.query(hashKey, rangeKeyCondition); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, QueryFilter... queryFilters) { - return queryDelegate.query(hashKey, rangeKeyCondition, queryFilters); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, String filterExpression, - Map nameMap, Map valueMap) { - return queryDelegate.query(hashKey, rangeKeyCondition, - filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, String projectionExpression, - String filterExpression, Map nameMap, - Map valueMap) { - return queryDelegate.query(hashKey, rangeKeyCondition, - projectionExpression, filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(QuerySpec spec) { - return queryDelegate.query(spec); - } - - @Override - public ItemCollection query( - String hashKeyName, Object hashKeyValue) { - return queryDelegate.query(hashKeyName, hashKeyValue); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition) { - return queryDelegate.query(hashKeyName, hashKeyValue, rangeKeyCondition); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - QueryFilter... queryFilters) { - return queryDelegate.query(hashKeyName, hashKeyValue, - rangeKeyCondition, queryFilters); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - String filterExpression, Map nameMap, - Map valueMap) { - return queryDelegate.query(hashKeyName, hashKeyValue, - rangeKeyCondition, filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - String filterExpression, String projectionExpression, - Map nameMap, Map valueMap) { - return queryDelegate.query(hashKeyName, hashKeyValue, - rangeKeyCondition, filterExpression, projectionExpression, - nameMap, valueMap); - } - - @Override - public ItemCollection query(KeyAttribute hashKey) { - return queryDelegate.query(hashKey); - } - - /** - * Updates the provisioned throughput for this global secondary index (GSI). - * Setting the throughput for an index helps you manage performance and is - * part of the provisioned throughput feature of DynamoDB. - *

    - * The provisioned throughput values can be upgraded or downgraded based on - * the maximums and minimums listed in the Limits section in the Amazon DynamoDB Developer Guide. - *

    - * This index must be a global secondary index and in the - * ACTIVE state for this operation to succeed. Updating a GSI - * is an asynchronous operation; while executing the operation, the index is - * in the UPDATING state. While the index is in the - * UPDATING state, the index still has the provisioned - * throughput from before the call. The new provisioned throughput setting - * is in effect only when the index returns to the ACTIVE state - * after the update is complete. - * - * @param provisionedThroughput - * target provisioned throughput - * - * @return the updated table description returned from DynamoDB. - */ - public TableDescription updateGsi( - ProvisionedThroughput provisionedThroughput) { - return table.updateTable(new UpdateTableSpec() - .withGlobalSecondaryIndexUpdates(GlobalSecondaryIndexUpdate.builder() - .update(UpdateGlobalSecondaryIndexAction.builder() - .indexName(indexName) - .provisionedThroughput(provisionedThroughput).build()) - .build())); - } - - /** - * Deletes this global secondary index (GSI) from the DynamoDB table. - * Involves network calls. - *

    - * This index must be a global secondary index and in the - * ACTIVE state for this operation to succeed. Deleting a GSI - * is an asynchronous operation; while executing the operation, the index is - * in the DELETING state. - * - * @return the updated table description returned from DynamoDB. - */ - public TableDescription deleteGsi() { - return table.updateTable(new UpdateTableSpec() - .withGlobalSecondaryIndexUpdates( - GlobalSecondaryIndexUpdate.builder() - .delete(DeleteGlobalSecondaryIndexAction.builder() - .indexName(indexName).build()) - .build())); - } - - /** - * A convenient blocking call that can be used, typically during index - * creation, to wait for the index to become active by polling the table - * every 5 seconds. - *

    - * Currently online index creation is only supported for Global Secondary - * Index (GSI). Calling this method on a Local Secondary Index (LSI) would - * result in IllegalArgumentException. - * - * @return the table description when the index has become active - * - * @throws IllegalArgumentException if the table is being deleted, or if - * the GSI is not being created or updated, or if the GSI doesn't exist - * @throws ResourceNotFoundException if the table doesn't exist - */ - public TableDescription waitForActive() throws InterruptedException { - final Table table = getTable(); - final String tableName = table.getTableName(); - final String indexName = getIndexName(); - retry: - for (; ; ) { - TableDescription desc = table.waitForActive(); - final List list = desc.globalSecondaryIndexes(); - if (list != null) { - for (GlobalSecondaryIndexDescription d : list) { - if (d.indexName().equals(indexName)) { - switch (d.indexStatus()) { - case ACTIVE: - return desc; - case CREATING: - case UPDATING: - Thread.sleep(SLEEP_TIME_MILLIS); - continue retry; - default: - throw new IllegalArgumentException( - "Global Secondary Index " - + indexName - + " is not being created or updated (with status=" - + d.indexStatusAsString() + ")"); - } - } - } - } - throw new IllegalArgumentException("Global Secondary Index " - + indexName + " does not exist in Table " + tableName + ")"); - } - } - - /** - * A convenient blocking call that can be used, typically during index - * deletion on an active table, to wait for the index to become deleted by - * polling the table every 5 seconds. - *

    - * Currently online index deletion is only supported for Global Secondary - * Index (GSI). The behavior of calling this method on a Local Secondary - * Index (LSI) would result in returning the latest table description. - * - * @return the table description if this GSI has been deleted; or null if - * the underlying table has been deleted. - * - * @throws IllegalArgumentException if the table is being deleted, or if the - * GSI is not being deleted. - * @throws ResourceNotFoundException if the table doesn't exist - */ - public TableDescription waitForDelete() throws InterruptedException { - final String indexName = getIndexName(); - retry: - for (; ; ) { - final TableDescription desc = getTable().waitForActive(); - List list = desc.globalSecondaryIndexes(); - if (list != null) { - for (GlobalSecondaryIndexDescription d : list) { - if (d.indexName().equals(indexName)) { - if (d.indexStatus() == IndexStatus.DELETING) { - Thread.sleep(SLEEP_TIME_MILLIS); - continue retry; - } - throw new IllegalArgumentException( - "Global Secondary Index " + indexName - + " is not being deleted (with status=" + d.indexStatusAsString() + ")"); - } - } - } - return desc; - } - } - - /** - * A convenient blocking call that can be used to wait on an index until it - * has either become active or deleted (ie no longer exists) by polling the - * table every 5 seconds. - *

    - * Currently online index creation/deletion is only supported for Global - * Secondary Index (GSI). The behavior of calling this method on a Local - * Secondary Index (LSI) would result in returning the latest table - * description. - * - * @return the table description when the index has become either active - * or deleted - * - * @throws IllegalArgumentException if the table is being deleted - * @throws ResourceNotFoundException if the table doesn't exist - */ - public TableDescription waitForActiveOrDelete() throws InterruptedException { - final Table table = getTable(); - final String indexName = getIndexName(); - retry: - for (; ; ) { - TableDescription desc = table.waitForActive(); - List list = desc.globalSecondaryIndexes(); - if (list != null) { - for (GlobalSecondaryIndexDescription d : desc.globalSecondaryIndexes()) { - if (d.indexName().equals(indexName)) { - if (d.indexStatus() == IndexStatus.ACTIVE) { - return desc; - } - Thread.sleep(SLEEP_TIME_MILLIS); - continue retry; - } - } - } - return desc; - } - } - - @Override - public ItemCollection scan(ScanFilter... scanFilters) { - return scanDelegate.scan(scanFilters); - } - - @Override - public ItemCollection scan(String filterExpression, - Map nameMap, Map valueMap) { - return scanDelegate.scan(filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection scan(String filterExpression, - String projectionExpression, Map nameMap, - Map valueMap) { - return scanDelegate.scan(filterExpression, projectionExpression, nameMap, valueMap); - } - - @Override - public ItemCollection scan(ScanSpec params) { - return scanDelegate.scan(params); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Item.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Item.java deleted file mode 100644 index 5c135de56989..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Item.java +++ /dev/null @@ -1,1445 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import static java.util.Arrays.asList; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.checkInvalidAttrName; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.checkInvalidAttribute; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.rejectNullInput; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.rejectNullOrEmptyInput; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.rejectNullValue; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.valToString; -import static software.amazon.awssdk.utils.BinaryUtils.copyAllBytesFrom; -import static software.amazon.awssdk.utils.BinaryUtils.copyBytesFrom; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import software.amazon.awssdk.core.util.json.JacksonUtils; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.document.internal.ItemValueConformer; -import software.amazon.awssdk.utils.BinaryUtils; - -/** - * An item in DynamoDB. An item is a collection of attributes. Each attribute - * has a name and a value. An attribute value can be one of the followings: - *

      - *
    • String
    • - *
    • Set<String>
    • - *
    • Number (including any subtypes and primitive types)
    • - *
    • Set<Number>
    • - *
    • byte[]
    • - *
    • Set<byte[]>
    • - *
    • ByteBuffer
    • - *
    • Set<ByteBuffer>
    • - *
    • Boolean or boolean
    • - *
    • null
    • - *
    • Map<String,T>, where T can be any type on this list but must not - * induce any circular reference
    • - *
    • List<T>, where T can be any type on this list but must not induce - * any circular reference
    • - *
    - * For an Item to be successfully persisted in DynamoDB, at a - * minimum the respective attributes for the primary key must be specified. - */ -public class Item { - private static final String DUPLICATE_VALUES_FOUND_IN_INPUT = "Duplicate values found in input"; - private static final ItemValueConformer VALUE_CONFORMER = new ItemValueConformer(); - private final Map attributes = new LinkedHashMap(); - - /** - * Convenient factory method - instantiates an Item from the - * given map. - * - * @param attributes - * simple Java types; not the DyanmoDB types - */ - public static Item fromMap(Map attributes) { - if (attributes == null) { - return null; - } - Item item = new Item(); - for (Map.Entry e : attributes.entrySet()) { - item.with(e.getKey(), e.getValue()); - } - return item; - } - - /** - * Convenient factory method - instantiates an Item from the - * given JSON string. - * - * @return an Item initialized from the given JSON document; - * or null if the input is null. - */ - public static Item fromJson(String json) { - if (json == null) { - return null; - } - @SuppressWarnings("unchecked") - Map map = (Map) - VALUE_CONFORMER.transform(JacksonUtils.fromJsonString(json, Map.class)); - return fromMap(map); - } - - /** - * Returns true if the specified attribute exists with a null value; false - * otherwise. - */ - public boolean isNull(String attrName) { - return attributes.containsKey(attrName) - && attributes.get(attrName) == null; - } - - /** - * Returns true if this item contains the specified attribute; false - * otherwise. - */ - public boolean isPresent(String attrName) { - return attributes.containsKey(attrName); - } - - /** - * Returns the value of the specified attribute in the current item as a - * string; or null if the attribute either doesn't exist or the attribute - * value is null. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public String getString(String attrName) { - Object val = attributes.get(attrName); - return valToString(val); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given string value. - */ - public Item withString(String attrName, String val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, val); - return this; - } - - /** - * Returns the value of the specified attribute in the current item as a - * BigDecimal; or null if the attribute either doesn't exist or - * the attribute value is null. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException if the attribute value is not a valid - * representation of a {@code BigDecimal}. - */ - public BigDecimal getNumber(String attrName) { - Object val = attributes.get(attrName); - return toBigDecimal(val); - } - - private BigDecimal toBigDecimal(Object val) { - if (val == null) { - return null; - } - return val instanceof BigDecimal - ? (BigDecimal) val - : new BigDecimal(val.toString()) - ; - } - - /** - * Returns the value of the specified attribute in the current item as an - * BigInteger; or null if the attribute doesn't exist. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException - * if the attribute value is null or not a valid representation - * of a {@code BigDecimal}. - */ - public BigInteger getBigInteger(String attrName) { - BigDecimal bd = getNumber(attrName); - return bd == null ? null : bd.toBigInteger(); - } - - /** - * Returns the value of the specified attribute in the current item as a - * short. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException - * if the attribute value is null or not a valid representation - * of a {@code BigDecimal}. - */ - public short getShort(String attrName) { - BigDecimal bd = getNumber(attrName); - if (bd == null) { - throw new NumberFormatException("value of " + attrName + " is null"); - } - return bd.shortValue(); - } - - /** - * Returns the value of the specified attribute in the current item as an - * int. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException - * if the attribute value is null or not a valid representation - * of a {@code BigDecimal}. - */ - public int getInt(String attrName) { - BigDecimal bd = getNumber(attrName); - if (bd == null) { - throw new NumberFormatException("value of " + attrName + " is null"); - } - return bd.intValue(); - } - - /** - * Returns the value of the specified attribute in the current item as an - * long. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException - * if the attribute value is null or not a valid representation - * of a {@code BigDecimal}. - */ - public long getLong(String attrName) { - BigDecimal bd = getNumber(attrName); - if (bd == null) { - throw new NumberFormatException("value of " + attrName + " is null"); - } - return bd.longValue(); - } - - /** - * Returns the value of the specified attribute in the current item as a - * float. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException - * if the attribute value is null or not a valid representation - * of a {@code BigDecimal}. - */ - public float getFloat(String attrName) { - BigDecimal bd = getNumber(attrName); - if (bd == null) { - throw new NumberFormatException("value of " + attrName + " is null"); - } - return bd.floatValue(); - } - - /** - * Returns the value of the specified attribute in the current item as a - * double. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws NumberFormatException - * if the attribute value is null or not a valid representation - * of a {@code BigDecimal}. - */ - public double getDouble(String attrName) { - BigDecimal bd = getNumber(attrName); - if (bd == null) { - throw new NumberFormatException("value of " + attrName + " is null"); - } - return bd.doubleValue(); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withNumber(String attrName, BigDecimal val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, val); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withNumber(String attrName, Number val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, toBigDecimal(val)); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withInt(String attrName, int val) { - checkInvalidAttrName(attrName); - return withNumber(attrName, Integer.valueOf(val)); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBigInteger(String attrName, BigInteger val) { - checkInvalidAttrName(attrName); - return withNumber(attrName, val); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withShort(String attrName, short val) { - checkInvalidAttrName(attrName); - return withNumber(attrName, Short.valueOf(val)); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withFloat(String attrName, float val) { - checkInvalidAttrName(attrName); - return withNumber(attrName, Float.valueOf(val)); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withDouble(String attrName, double val) { - checkInvalidAttrName(attrName); - return withNumber(attrName, Double.valueOf(val)); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withLong(String attrName, long val) { - checkInvalidAttrName(attrName); - return withNumber(attrName, Long.valueOf(val)); - } - - /** - * Returns the value of the specified attribute in the current item as a - * byte array; or null if the attribute either doesn't exist or the - * attribute value is null. - * - * @throws UnsupportedOperationException - * If the attribute value involves a byte buffer which is not - * backed by an accessible array - * - * @throws IncompatibleTypeException - * if the attribute value cannot be converted into a byte array - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public byte[] getBinary(String attrName) { - Object val = attributes.get(attrName); - return toByteArray(val); - } - - /** - * Returns the value of the specified attribute in the current item as a - * ByteBuffer; or null if the attribute either doesn't exist or - * the attribute value is null. - * - * @throws IncompatibleTypeException - * if the attribute value cannot be converted into a byte array - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public ByteBuffer getByteBuffer(String attrName) { - Object val = attributes.get(attrName); - return toByteBuffer(val); - } - - /** - * This method is assumed to be only called from a getter method, but NOT - * from a setter method. - */ - private byte[] toByteArray(Object val) { - if (val == null) { - return null; - } - if (val instanceof byte[]) { - return (byte[]) val; - } - if (val instanceof ByteBuffer) { - // Defensive code but execution should never get here. The internal - // representation of binary should always be - // byte[], not ByteBuffer. This allows Item to be converted into - // a JSON string via Jackson without causing trouble. - return copyAllBytesFrom((ByteBuffer) val); - } - throw new IncompatibleTypeException(val.getClass() - + " cannot be converted into a byte array"); - } - - private ByteBuffer toByteBuffer(Object val) { - if (val == null) { - return null; - } - if (val instanceof byte[]) { - return ByteBuffer.wrap((byte[]) val); - } - if (val instanceof ByteBuffer) { - // Defensive code but execution should never get here. The internal - // representation of binary should always be - // byte[], not ByteBuffer. This allows Item to be converted into - // a JSON string via Jackson without causing trouble. - return (ByteBuffer) val; - } - throw new IncompatibleTypeException(val.getClass() - + " cannot be converted into a ByteBuffer"); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBinary(String attrName, byte[] val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, val); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBinary(String attrName, ByteBuffer val) { - checkInvalidAttribute(attrName, val); - // convert ByteBuffer to bytes to keep Jackson happy - attributes.put(attrName, copyBytesFrom(val)); - return this; - } - - /** - * Returns the value of the specified attribute in the current item as a set - * of strings; or null if the attribute either doesn't exist or the - * attribute value is null. - * - * @throws IncompatibleTypeException - * if the attribute value cannot be converted into a set of - * strings because of duplicate elements - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Set getStringSet(String attrName) { - Object val = attributes.get(attrName); - if (val == null) { - return null; - } - Set stringSet = new LinkedHashSet(); - if (val instanceof Collection) { - Collection col = (Collection) val; - if (col.size() == 0) { - return stringSet; - } - for (Object element : col) { - String s = element == null ? null : valToString(element); - if (!stringSet.add(s)) { - throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of strings because " + - "of duplicate elements"); - } - } - return stringSet; - } - stringSet.add(valToString(val)); - return stringSet; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withStringSet(String attrName, Set val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, val); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withStringSet(String attrName, String... val) { - checkInvalidAttribute(attrName, val); - Set strSet = new LinkedHashSet(asList(val)); - if (strSet.size() != val.length) { - throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); - } - attributes.put(attrName, strSet); - return this; - } - - /** - * Returns the value of the specified attribute in the current item as a set - * of BigDecimal's; or null if the attribute either doesn't exist or the - * attribute value is null. - * - * @throws NumberFormatException - * if the attribute involves a value that is not a valid - * representation of a {@code BigDecimal}. - * - * @throws IncompatibleTypeException - * if the attribute value cannot be converted into a set of - * BigDecimal's because of duplicate elements - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Set getNumberSet(String attrName) { - Object val = attributes.get(attrName); - if (val == null) { - return null; - } - Set numSet = new LinkedHashSet(); - if (val instanceof Collection) { - Collection col = (Collection) val; - if (col.size() == 0) { - return numSet; - } - for (Object element : col) { - BigDecimal bd = toBigDecimal(element); - if (!numSet.add(bd)) { - throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of BigDecimal's " + - "because of duplicate elements"); - } - } - return numSet; - } else if (val instanceof BigDecimal) { - numSet.add((BigDecimal) val); - return numSet; - } else { - numSet.add(new BigDecimal(val.toString())); - return numSet; - } - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBigDecimalSet(String attrName, Set val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, val); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBigDecimalSet(String attrName, BigDecimal... vals) { - checkInvalidAttribute(attrName, vals); - Set set = new LinkedHashSet(asList(vals)); - if (set.size() != vals.length) { - throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); - } - attributes.put(attrName, set); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withNumberSet(String attrName, Number... vals) { - checkInvalidAttribute(attrName, vals); - Set set = InternalUtils.toBigDecimalSet(vals); - if (set.size() != vals.length) { - throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); - } - return withBigDecimalSet(attrName, set); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withNumberSet(String attrName, Set vals) { - checkInvalidAttribute(attrName, vals); - Set set = InternalUtils.toBigDecimalSet(vals); - if (set.size() != vals.size()) { - throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); - } - return withBigDecimalSet(attrName, set); - } - - /** - * Returns the value of the specified attribute in the current item as a set - * of byte arrays; or null if the attribute either doesn't exist or the - * attribute value is null. - * - * @throws IncompatibleTypeException - * if the attribute value cannot be converted into a set of byte - * arrays - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Set getBinarySet(String attrName) { - Object val = attributes.get(attrName); - if (val == null) { - return null; - } - Set binarySet = new LinkedHashSet(); - if (val instanceof Collection) { - Collection col = (Collection) val; - if (col.size() == 0) { - return binarySet; - } - for (Object element : col) { - byte[] ba = toByteArray(element); - if (!binarySet.add(ba)) { - throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of byte arrays " + - "because of duplicate elements"); - } - } - return binarySet; - } else if (val instanceof byte[]) { - binarySet.add((byte[]) val); - return binarySet; - } else if (val instanceof ByteBuffer) { - // Defensive code but execution should never get here. The internal - // representation of binary should always be - // byte[], not ByteBuffer. This allows Item to be converted into - // a JSON string via Jackson without causing trouble. - ByteBuffer bb = (ByteBuffer) val; - binarySet.add(copyAllBytesFrom(bb)); - return binarySet; - } - throw new IncompatibleTypeException(val.getClass() - + " cannot be converted into a set of byte arrays"); - } - - /** - * Returns the value of the specified attribute in the current item as a set - * of ByteBuffer; or null if the attribute either doesn't exist - * or the attribute value is null. - * - * @throws IncompatibleTypeException - * if the attribute value cannot be converted into a set of - * ByteBuffer - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Set getByteBufferSet(String attrName) { - Object val = attributes.get(attrName); - if (val == null) { - return null; - } - Set binarySet = new LinkedHashSet(); - if (val instanceof Collection) { - Collection col = (Collection) val; - if (col.size() == 0) { - return binarySet; - } - for (Object element : col) { - ByteBuffer ba = toByteBuffer(element); - if (!binarySet.add(ba)) { - throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of ByteBuffer " + - "because of duplicate elements"); - } - } - return binarySet; - } else if (val instanceof ByteBuffer) { - // Defensive code but execution should never get here. The internal - // representation of binary should always be - // byte[], not ByteBuffer. This allows Item to be converted into - // a JSON string via Jackson without causing trouble. - binarySet.add((ByteBuffer) val); - return binarySet; - } else if (val instanceof byte[]) { - binarySet.add(ByteBuffer.wrap((byte[]) val)); - return binarySet; - } - throw new IncompatibleTypeException(val.getClass() - + " cannot be converted into a set of ByteBuffer"); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBinarySet(String attrName, Set val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, val); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withByteBufferSet(String attrName, Set val) { - checkInvalidAttribute(attrName, val); - // convert ByteBuffer to bytes to keep Jackson happy - Set set = new LinkedHashSet(val.size()); - for (ByteBuffer bb : val) { - set.add(copyBytesFrom(bb)); - } - attributes.put(attrName, set); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBinarySet(String attrName, byte[]... vals) { - checkInvalidAttribute(attrName, vals); - Set set = new LinkedHashSet(asList(vals)); - if (set.size() != vals.length) { - throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); - } - attributes.put(attrName, set); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withBinarySet(String attrName, ByteBuffer... vals) { - checkInvalidAttribute(attrName, vals); - // convert ByteBuffer to bytes to keep Jackson happy - Set set = new LinkedHashSet(vals.length); - for (ByteBuffer bb : vals) { - set.add(copyBytesFrom(bb)); - } - if (set.size() != vals.length) { - throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); - } - attributes.put(attrName, set); - return this; - } - - /** - * Returns the value of the specified attribute in the current item as a set - * of T's.; or null if the attribute either doesn't exist or - * the attribute value is null. - * - * @throws ClassCastException - * if the attribute involves a value that cannot be casted to - * T - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public List getList(String attrName) { - Object val = attributes.get(attrName); - if (val == null) { - return null; - } - if (val instanceof List) { - @SuppressWarnings("unchecked") - List ret = (List) val; - return ret; - } - List list = new ArrayList(); - if (val instanceof Collection) { - Collection col = (Collection) val; - for (Object element : col) { - @SuppressWarnings("unchecked") - T t = (T) element; - list.add(t); - } - return list; - } - @SuppressWarnings("unchecked") - T t = (T) val; - list.add(t); - return list; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withList(String attrName, List val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, VALUE_CONFORMER.transform(val)); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given values as a list. - */ - public Item withList(String attrName, Object... vals) { - checkInvalidAttribute(attrName, vals); - List listIn = asList(vals); - attributes.put(attrName, VALUE_CONFORMER.transform(listIn)); - return this; - } - - /** - * Returns the value of the specified attribute in the current item as a map - * of string-to-T's; or null if the attribute either doesn't - * exist or the attribute value is null. Note that any numeric type of a - * map is always canonicalized into BigDecimal, and therefore - * if T referred to a Number type, it would need - * to be BigDecimal to avoid a class cast exception. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws ClassCastException - * if the attribute is not a map of string to T - */ - @SuppressWarnings("unchecked") - public Map getMap(String attrName) { - return (Map) attributes.get(attrName); - } - - /** - * Convenient method to return the specified attribute in the current item - * as a (copy of) map of string-to-T's where T must be a - * subclass of Number; or null if the attribute doesn't - * exist. - * - * @param attrName - * the attribute name - * @param valueType - * the specific number type of the value to be returned. - * Currently, only
      - *
    • Short
    • - *
    • Integer
    • - *
    • Long
    • - *
    • Float
    • - *
    • Double
    • - *
    • Number
    • - *
    • BigDecimal
    • - *
    • BigInteger
    • - *
    are supported. - * - * @throws UnsupportedOperationException - * if the value type is not supported - * @throws ClassCastException - * if the attribute is not a map of string to numbers - */ - @SuppressWarnings("unchecked") - public Map getMapOfNumbers(String attrName, - Class valueType) { - if (valueType == Short.class - || valueType == Integer.class - || valueType == Long.class - || valueType == Float.class - || valueType == Double.class - || valueType == Number.class - || valueType == BigDecimal.class - || valueType == BigInteger.class) { - final Map src = - (Map) attributes.get(attrName); - if (src == null) { - return null; - } - final Map dst = new LinkedHashMap(src.size()); - for (Map.Entry e : src.entrySet()) { - final String key = e.getKey(); - final BigDecimal val = e.getValue(); - if (val == null) { - dst.put(key, null); - } else if (valueType == Short.class) { - dst.put(key, (T) Short.valueOf(val.shortValue())); - } else if (valueType == Integer.class) { - dst.put(key, (T) Integer.valueOf(val.intValue())); - } else if (valueType == Long.class) { - dst.put(key, (T) Long.valueOf(val.longValue())); - } else if (valueType == Float.class) { - dst.put(key, (T) Float.valueOf(val.floatValue())); - } else if (valueType == Double.class) { - dst.put(key, (T) Double.valueOf(val.doubleValue())); - } else if (valueType == BigDecimal.class || valueType == Number.class) { - dst.put(key, (T) val); - } else if (valueType == BigInteger.class) { - dst.put(key, (T) val.toBigInteger()); - } - } - return dst; - } else { - throw new UnsupportedOperationException("Value type " + valueType - + " is not currently supported"); - } - } - - /** - * Convenient method to return the value of the specified attribute in the - * current item as a map of string-to-Object's; or null if the - * attribute either doesn't exist or the attribute value is null. Note that - * any numeric type of the map will be returned as BigDecimal. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - * - * @throws ClassCastException if the attribute is not a map - */ - @SuppressWarnings("unchecked") - public Map getRawMap(String attrName) { - return (Map) attributes.get(attrName); - } - - /** - * Sets the value of the specified attribute in the current item to the - * given value. - */ - public Item withMap(String attrName, Map val) { - checkInvalidAttribute(attrName, val); - attributes.put(attrName, VALUE_CONFORMER.transform(val)); - return this; - } - - /** - * Sets the value of the specified attribute in the current item to the - * given JSON document in the form of a string. - */ - public Item withJson(String attrName, String json) { - checkInvalidAttribute(attrName, json); - attributes.put(attrName, - VALUE_CONFORMER.transform(JacksonUtils.fromJsonString(json, Object.class))); - return this; - } - - /** - * Returns the value of the specified attribute in the current item as a - * JSON string; or null if the attribute either doesn't - * exist or the attribute value is null. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public String getJson(String attrName) { - checkInvalidAttrName(attrName); - Object val = attributes.get(attrName); - return val == null ? null : JacksonUtils.toJsonString(val); - } - - /** - * Returns the value of the specified attribute in the current item as a - * JSON string with pretty indentation; or null if the attribute either - * doesn't exist or the attribute value is null. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public String getJsonPretty(String attrName) { - checkInvalidAttrName(attrName); - Object val = attributes.get(attrName); - return val == null ? null : JacksonUtils.toJsonPrettyString(val); - } - - /** - * Returns the value of the specified attribute in the current item as a - * non-null Boolean. - * - * @throws IncompatibleTypeException - * if either the attribute doesn't exist or if the attribute - * value cannot be converted into a non-null Boolean value - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Boolean getBool(String attrName) { - final Object val = attributes.get(attrName); - if (val instanceof Boolean) { - return (Boolean) val; - } - if (val instanceof String) { - if ("1".equals(val)) { - return true; - } - if ("0".equals(val)) { - return false; - } - return Boolean.valueOf((String) val); - } - throw new IncompatibleTypeException("Value of attribute " + attrName - + " of type " + getTypeOf(attrName) - + " cannot be converted into a boolean value"); - } - - /** - * Returns the value of the specified attribute in the current item as a - * primitive boolean. - * - * @throws IncompatibleTypeException - * if either the attribute doesn't exist or if the attribute - * value cannot be converted into a boolean value - */ - public boolean getBoolean(String attrName) { - final Boolean b = getBool(attrName); - return b.booleanValue(); - } - - /** - * Sets the value of the specified attribute in the current item to the - * boolean value. - */ - public Item withBoolean(String attrName, boolean val) { - checkInvalidAttrName(attrName); - attributes.put(attrName, Boolean.valueOf(val)); - return this; - } - - /** - * Sets the value of the specified attribute to null. - */ - public Item withNull(String attrName) { - checkInvalidAttrName(attrName); - attributes.put(attrName, null); - return this; - } - - /** - * Sets the value of the specified attribute to the given value. An - * attribute value can be a - *
      - *
    • Number
    • - *
    • String
    • - *
    • binary (ie byte array or byte buffer)
    • - *
    • boolean
    • - *
    • null
    • - *
    • list (of any of the types on this list)
    • - *
    • map (with string key to value of any of the types on this list)
    • - *
    • set (of any of the types on this list)
    • - *
    - */ - public Item with(String attrName, Object val) { - if (val == null) { - return withNull(attrName); - } - if (val instanceof String) { - return withString(attrName, (String) val); - } - if (val instanceof Number) { - return withNumber(attrName, (Number) val); - } - if (val instanceof byte[]) { - return withBinary(attrName, (byte[]) val); - } - if (val instanceof ByteBuffer) { - return withBinary(attrName, (ByteBuffer) val); - } - if (val instanceof Boolean) { - return withBoolean(attrName, (Boolean) val); - } - if (val instanceof List) { - return withList(attrName, (List) val); - } - if (val instanceof Map) { - @SuppressWarnings("unchecked") - Map map = (Map) val; - return withMap(attrName, map); - } - if (val instanceof Set) { - Set set = (Set) val; - // Treat an empty set as a set of String - if (set.size() == 0) { - @SuppressWarnings("unchecked") - Set ss = (Set) val; - return withStringSet(attrName, ss); - } - // Try to locate the first non-null element and use that as the - // representative type - Object representative = null; - for (Object o : set) { - if (o != null) { - representative = o; - } - } - // If all elements are null, treat the element type as String - if (representative == null || representative instanceof String) { - @SuppressWarnings("unchecked") - Set ss = (Set) val; - return withStringSet(attrName, ss); - } - if (representative instanceof Number) { - @SuppressWarnings("unchecked") - Set ns = (Set) val; - return withNumberSet(attrName, ns); - } - if (representative instanceof byte[]) { - @SuppressWarnings("unchecked") - Set bs = (Set) val; - return withBinarySet(attrName, bs); - } - if (representative instanceof ByteBuffer) { - @SuppressWarnings("unchecked") - Set bs = (Set) val; - return withByteBufferSet(attrName, bs); - } - throw new UnsupportedOperationException("Set of " - + representative.getClass() + " is not currently supported"); - } - throw new UnsupportedOperationException("Input type " - + val.getClass() + " is not currently supported"); - } - - /** - * Convenient methods - sets the attributes of this item from the given - * key attributes. - */ - public Item withPrimaryKey(PrimaryKey primaryKey) { - rejectNullValue(primaryKey); - if (primaryKey.getComponents().size() == 0) { - throw new IllegalArgumentException("primary key must not be empty"); - } - for (KeyAttribute ka : primaryKey.getComponents()) { - this.with(ka.name(), ka.value()); - } - return this; - } - - /** - * Convenient method to set the attributes of this item from the given - * hash-only primary key name and value. - */ - public Item withPrimaryKey(String hashKeyName, Object hashKeyValue) { - return withKeyComponent(hashKeyName, hashKeyValue); - } - - /** - * Convenient method to set the attributes of this item from the given - * hash and range primary key. - */ - public Item withPrimaryKey(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return withKeyComponent(hashKeyName, hashKeyValue) - .withKeyComponent(rangeKeyName, rangeKeyValue); - } - - /** - * Convenient methods - sets the attributes of this item from the specified - * key components. - */ - public Item withKeyComponents(KeyAttribute... components) { - rejectNullOrEmptyInput(components); - for (KeyAttribute ka : components) { - rejectNullValue(ka); - this.with(ka.name(), ka.value()); - } - return this; - } - - /** - * Convenient methods - sets an attribute of this item for the specified - * key attribute name and value. - */ - public Item withKeyComponent(String keyAttrName, Object keyAttrValue) { - return with(keyAttrName, keyAttrValue); - } - - /** - * Returns the value of the specified attribute in the current item as an - * object; or null if the attribute either doesn't exist or the attribute - * value is null. - *

    - * An attribute value can be a - *

      - *
    • Number
    • - *
    • String
    • - *
    • binary (ie byte array or byte buffer)
    • - *
    • boolean
    • - *
    • null
    • - *
    • list (of any of the types on this list)
    • - *
    • map (with string key to value of any of the types on this list)
    • - *
    • set (of any of the types on this list)
    • - *
    - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Object get(String attrName) { - return attributes.get(attrName); - } - - /** - * Returns the type of the specified attribute in the current item; or null - * if the attribute either doesn't exist or the attribute value is null. - * - * @see #isNull(String) #isNull(String) to check if the attribute value is - * null. - * @see #isPresent(String) #isPresent(String) to check if the attribute - * value is present. - */ - public Class getTypeOf(String attrName) { - Object val = attributes.get(attrName); - return val == null ? null : val.getClass(); - } - - /** - * Removes the specified attribute from the current item. - */ - public Item removeAttribute(String attrName) { - checkInvalidAttrName(attrName); - attributes.remove(attrName); - return this; - } - - /** - * Returns all attributes of the current item. - */ - public Iterable> attributes() { - return new LinkedHashMap(attributes).entrySet(); - } - - /** - * Returns true if this item has the specified attribute; false otherwise. - */ - public boolean hasAttribute(String attrName) { - return attributes.containsKey(attrName); - } - - /** - * Returns all attributes of the current item as a map. - */ - public Map asMap() { - return new LinkedHashMap(attributes); - } - - /** - * Returns the number of attributes of this item. - */ - public int numberOfAttributes() { - return attributes.size(); - } - - /** - * Returns this item as a JSON string. Note all binary data will become - * base-64 encoded in the resultant string. - */ - public String toJson() { - return JacksonUtils.toJsonString(this.attributes); - } - - /** - * Utility method to decode the designated binary attributes from base-64 - * encoding; converting binary lists into binary sets. - * - * @param binaryAttrNames - * names of binary attributes or binary set attributes currently - * base-64 encoded (typically when converted from a JSON string.) - * - * @see #fromJson(String) - */ - public Item base64Decode(String... binaryAttrNames) { - rejectNullInput(binaryAttrNames); - // Verify all attributes are good - for (String attrName : binaryAttrNames) { - checkInvalidAttrName(attrName); - if (String.class == getTypeOf(attrName)) { - String b64 = getString(attrName); - BinaryUtils.fromBase64(b64); - } else { - Set b64s = getStringSet(attrName); - for (String b64 : b64s) { - BinaryUtils.fromBase64(b64); - } - } - } - // Decodes b64 into binary - for (String attrName : binaryAttrNames) { - if (String.class == getTypeOf(attrName)) { - String b64 = getString(attrName); - byte[] bytes = BinaryUtils.fromBase64(b64); - withBinary(attrName, bytes); - } else { - Set b64s = getStringSet(attrName); - Set binarySet = new LinkedHashSet(b64s.size()); - for (String b64 : b64s) { - binarySet.add(BinaryUtils.fromBase64(b64)); - } - withBinarySet(attrName, binarySet); - } - } - return this; - } - - /** - * Utility method to converts the designated attributes from - * List into Set, throwing - * IllegalArgumentException should there be duplicate elements. - * - * @param listAttrNames - * names of attributes to be converted. - * - * @see #fromJson(String) - */ - public Item convertListsToSets(String... listAttrNames) { - rejectNullInput(listAttrNames); - // Verify all attributes are good - for (String attrName : listAttrNames) { - checkInvalidAttrName(attrName); - if (List.class.isAssignableFrom(getTypeOf(attrName))) { - List list = getList(attrName); - if (list != null) { - for (Object e : list) { - if (e instanceof String) { - Set ss = getStringSet(attrName); - if (list.size() != ss.size()) { - throw new IllegalArgumentException("List cannot be converted to Set due to duplicate elements"); - } - } else if (e instanceof Number) { - Set ss = getNumberSet(attrName); - if (list.size() != ss.size()) { - throw new IllegalArgumentException("List cannot be converted to Set due to duplicate elements"); - } - } else if (e instanceof byte[]) { - Set ss = getBinarySet(attrName); - if (list.size() != ss.size()) { - throw new IllegalArgumentException("List cannot be converted to Set due to duplicate elements"); - } - } - } - } - } else { - throw new IllegalArgumentException("Attribute " + attrName + " is not a list"); - } - } - // Do the conversion - for (String attrName : listAttrNames) { - checkInvalidAttrName(attrName); - List list = getList(attrName); - if (list != null) { - boolean converted = false; - for (Object e : list) { - if (e instanceof String) { - Set set = getStringSet(attrName); - withStringSet(attrName, set); - converted = true; - break; - } else if (e instanceof Number) { - Set set = getNumberSet(attrName); - withBigDecimalSet(attrName, set); - converted = true; - break; - } else if (e instanceof byte[]) { - Set set = getBinarySet(attrName); - withBinarySet(attrName, set); - converted = true; - break; - } - } - if (!converted) { - // All elements are null. So treat it as a String set. - Set set = getStringSet(attrName); - withStringSet(attrName, set); - } - } - } - return this; - } - - /** - * Returns this item as a pretty JSON string. Note all binary data will - * become base-64 encoded in the resultant string. - */ - public String toJsonPretty() { - return JacksonUtils.toJsonPrettyString(this.attributes); - } - - @Override - public String toString() { - return "{ Item: " + attributes.toString() + " }"; - } - - @Override - public int hashCode() { - return attributes.hashCode(); - } - - @Override - public boolean equals(Object in) { - if (in instanceof Item) { - Item that = (Item) in; - return this.attributes.equals(that.attributes); - } else { - return false; - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemCollection.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemCollection.java deleted file mode 100644 index 1f655209c04b..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemCollection.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.internal.PageBasedCollection; -import software.amazon.awssdk.services.dynamodb.document.internal.PageIterable; -import software.amazon.awssdk.services.dynamodb.model.Capacity; -import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity; - -/** - * A collection of Item's. - * - * An ItemCollection object maintains a cursor pointing to its - * current pages of data. Initially the cursor is positioned before the first page. - * The next method moves the cursor to the next row, and because it returns - * false when there are no more rows in the ItemCollection object, - * it can be used in a while loop to iterate through the collection. - * - * Network calls can be triggered when the collection is iterated across page - * boundaries. - * - * @param low level result type - */ -public abstract class ItemCollection extends PageBasedCollection { - private int accumulatedItemCount; - private int accumulatedScannedCount; - private ConsumedCapacity accumulatedConsumedCapacity; - - protected final void accumulateStats(ConsumedCapacity consumedCapacity, - Integer count, Integer scannedCount) { - if (consumedCapacity != null) { - if (accumulatedConsumedCapacity == null) { - // Create a new consumed capacity by cloning the one passed in - ConsumedCapacity.Builder cloneBuilder = ConsumedCapacity.builder(); - - cloneBuilder.capacityUnits(consumedCapacity.capacityUnits()); - cloneBuilder.globalSecondaryIndexes( - clone(consumedCapacity.globalSecondaryIndexes())); - cloneBuilder.localSecondaryIndexes( - clone(consumedCapacity.localSecondaryIndexes())); - cloneBuilder.table(clone(consumedCapacity.table())); - cloneBuilder.tableName(consumedCapacity.tableName()); - - this.accumulatedConsumedCapacity = cloneBuilder.build(); - } else { - // Accumulate the capacity units - final Double capunit = accumulatedConsumedCapacity.capacityUnits(); - final Double delta = consumedCapacity.capacityUnits(); - if (capunit == null) { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder().capacityUnits(delta).build(); - } else { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder().capacityUnits(capunit.doubleValue() - + (delta == null ? 0 : delta.doubleValue())).build(); - } - // Accumulate the GSI capacities - final Map gsi = accumulatedConsumedCapacity.globalSecondaryIndexes(); - if (gsi == null) { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder().globalSecondaryIndexes( - clone(consumedCapacity.globalSecondaryIndexes())).build(); - } else { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder().globalSecondaryIndexes(add( - consumedCapacity.globalSecondaryIndexes(), - clone(accumulatedConsumedCapacity.globalSecondaryIndexes()))).build(); - } - // Accumulate the LSI capacities - final Map lsi = accumulatedConsumedCapacity.localSecondaryIndexes(); - if (lsi == null) { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder().localSecondaryIndexes( - clone(consumedCapacity.localSecondaryIndexes())).build(); - } else { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder().localSecondaryIndexes(add( - consumedCapacity.localSecondaryIndexes(), - clone(accumulatedConsumedCapacity.localSecondaryIndexes()))).build(); - } - // Accumulate table capacity - final Capacity tableCapacity = accumulatedConsumedCapacity.table(); - if (tableCapacity == null) { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder() - .table(clone(consumedCapacity.table())) - .build(); - } else { - accumulatedConsumedCapacity = accumulatedConsumedCapacity.toBuilder() - .table(add(consumedCapacity.table(), - accumulatedConsumedCapacity.table())).build(); - } - } - } - if (count != null) { - this.accumulatedItemCount += count.intValue(); - } - if (scannedCount != null) { - this.accumulatedScannedCount += scannedCount.intValue(); - } - } - - private Map add(Map from, Map to) { - if (to == null) { - return clone(from); - } - if (from != null) { - for (Map.Entry entryFrom : from.entrySet()) { - final String key = entryFrom.getKey(); - final Capacity tocap = to.get(key); - final Capacity fromcap = entryFrom.getValue(); - if (tocap == null) { - to.put(key, clone(fromcap)); - } else { - to.put(key, Capacity.builder().capacityUnits( - doubleOf(tocap) + doubleOf(fromcap)).build()); - } - } - } - return to; - } - - private Capacity add(final Capacity from, final Capacity to) { - return Capacity.builder().capacityUnits(doubleOf(from) + doubleOf(to)).build(); - } - - private Map clone(Map capacityMap) { - if (capacityMap == null) { - return null; - } - Map clone = - new HashMap(capacityMap.size()); - for (Map.Entry e : capacityMap.entrySet()) { - clone.put(e.getKey(), clone(e.getValue())); - } - return clone; - } - - private Capacity clone(Capacity capacity) { - return capacity == null - ? null - : Capacity.builder().capacityUnits(capacity.capacityUnits()).build(); - } - - private double doubleOf(Capacity cap) { - if (cap == null) { - return 0.0; - } - Double val = cap.capacityUnits(); - return val == null ? 0.0 : val.doubleValue(); - } - - /** - * Returns the count of items accumulated so far. - * @deprecated This method returns the accumulated count and not the total count. - * Use {@link #getAccumulatedItemCount} instead. - */ - @Deprecated - public int getTotalCount() { - return getAccumulatedItemCount(); - } - - /** - * Returns the count of items accumulated so far. - */ - public int getAccumulatedItemCount() { - return accumulatedItemCount; - } - - /** - * Returns the scanned count accumulated so far. - * @deprecated This method returns the accumulated count and not the total count. - * Use {@link #getAccumulatedScannedCount} instead. - */ - @Deprecated - public int getTotalScannedCount() { - return getAccumulatedScannedCount(); - } - - /** - * Returns the scanned count accumulated so far. - */ - public int getAccumulatedScannedCount() { - return accumulatedScannedCount; - } - - /** - * Returns the consumed capacity accumulated so far. - * @deprecated This method returns the accumulated consumed capacity and not the total. - * Use {@link #getAccumulatedScannedCount} instead. - */ - @Deprecated - public ConsumedCapacity getTotalConsumedCapacity() { - return getAccumulatedConsumedCapacity(); - } - - /** - * Returns the consumed capacity accumulated so far. - */ - public ConsumedCapacity getAccumulatedConsumedCapacity() { - return accumulatedConsumedCapacity; - } - - // Overriding these just so javadocs will show up. - - /** - * Returns an {@code Iterable>} that iterates over pages of - * items from this collection. Each call to {@code Iterator.next} on an - * {@code Iterator} returned from this {@code Iterable} results in exactly - * one call to DynamoDB to retrieve a single page of results. - *

    - * - * ItemCollection<QueryResponse> collection = ...; - * for (Page<Item> page : collection.pages()) { - * processItems(page); - * - * ConsumedCapacity consumedCapacity = - * page.getLowLevelResult().getConsumedCapacity(); - * - * Thread.sleep(getBackoff(consumedCapacity.getCapacityUnits())); - * } - * - *

    - * The use of the internal/undocumented {@code PageIterable} class instead - * of {@code Iterable} in the public interface here is retained for - * backwards compatibility. It doesn't expose any methods beyond those - * of the {@code Iterable} interface. This method will be changed to return - * an {@code Iterable>} directly in a future release of the - * SDK. - * - * @see Page - */ - @Override - public PageIterable pages() { - return super.pages(); - } - - /** - * Returns the maximum number of resources to be retrieved in this - * collection; or null if there is no limit. - */ - @Override - public abstract Integer getMaxResultSize(); - - /** - * Returns the low-level result last retrieved (for the current page) from - * the server side; or null if there has yet no calls to the server. - */ - @Override - public R getLastLowLevelResult() { - return super.getLastLowLevelResult(); - } - - /** - * Used to register a listener for the event of receiving a low-level result - * from the server side. - * - * @param listener - * listener to be registered. If null, a "none" listener will be - * set. - * @return the previously registered listener. The return value is never - * null. - */ - @Override - public LowLevelResultListener registerLowLevelResultListener( - LowLevelResultListener listener) { - - return super.registerLowLevelResultListener(listener); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemTest.java deleted file mode 100644 index 95c62194a295..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemTest.java +++ /dev/null @@ -1,921 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.document.utils.FluentArrayList; -import software.amazon.awssdk.services.dynamodb.document.utils.FluentHashSet; -import software.amazon.awssdk.services.dynamodb.document.utils.ValueMap; -import software.amazon.awssdk.utils.BinaryUtils; - -public class ItemTest { - - @Test - public void jsonDoubleMax() { - double[] values = { - Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE, Double.MIN_NORMAL - }; - for (double val : values) { - doJsonDoubleTest(val); - } - } - - private void doJsonDoubleTest(double value) { - Item item1 = new Item().withDouble("double", value); - final String json = item1.toJsonPretty(); - System.out.println(json); - Item item2 = Item.fromJson(json); - assertEquals(json, item2.toJsonPretty()); - } - - @Test - public void isNull() { - Item item = new Item(); - assertFalse(item.isNull("test")); - - item.withNull("test"); - assertTrue(item.isNull("test")); - - item.removeAttribute("test"); - assertFalse(item.isNull("test")); - - item.withString("test", "foo"); - assertFalse(item.isNull("test")); - assertEquals("foo", item.getString("test")); - } - - @Test - public void is_null() { - Item item = new Item(); - assertFalse(item.isNull("test")); - - item.with("test", null); - assertTrue(item.isNull("test")); - - assertNull(item.get("test")); - - item.removeAttribute("test"); - assertFalse(item.isNull("test")); - - assertNull(item.get("test")); - } - - @Test - public void isPresent() { - Item item = new Item(); - assertFalse(item.isPresent("test")); - - item.withNull("test"); - assertTrue(item.isPresent("test")); - - item.removeAttribute("test"); - assertFalse(item.isPresent("test")); - - item.withString("test", "foo"); - assertTrue(item.isPresent("test")); - } - - @Test - public void toBigDecimal_Null() { - Item item = new Item(); - assertNull(item.getNumber("test")); - } - - @Test(expected = NumberFormatException.class) - public void getInt_Null() { - Item item = new Item(); - item.getInt("test"); - } - - @Test(expected = NumberFormatException.class) - public void getLong_Null() { - Item item = new Item(); - item.getLong("test"); - } - - @Test(expected = NumberFormatException.class) - public void getNumber_NonNumber() { - Item item = new Item(); - item.withString("test", "foo"); - item.getNumber("test"); - } - - @Test - public void withNumber() { - Item item = new Item(); - item.withNumber("test", BigDecimal.ONE); - assertSame(BigDecimal.ONE, item.getNumber("test")); - assertTrue(1 == item.getInt("test")); - assertTrue(1L == item.getLong("test")); - } - - @Test - public void withLong() { - Item item = new Item(); - item.withLong("test", 123L); - assertTrue(123L == item.getLong("test")); - } - - @Test - public void toByteArray() { - Item item = new Item(); - assertNull(item.getBinary("test")); - byte[] bytes = {1, 2, 3}; - item.withBinary("test", bytes); - assertTrue(Arrays.equals(bytes, item.getBinary("test"))); - ByteBuffer bb = ByteBuffer.wrap(bytes); - item.withBinary("test", bb); - assertTrue(byte[].class == item.getTypeOf("test")); - assertTrue(Arrays.equals(bytes, item.getBinary("test"))); - assertTrue(Arrays.equals(bytes, item.getByteBuffer("test").array())); - } - - @Test(expected = IncompatibleTypeException.class) - public void toByteArray_IncompatibleTypeException() { - Item item = new Item(); - item.withString("test", "foo"); - item.getBinary("test"); - } - - @Test - public void toByteBuffer() { - Item item = new Item(); - assertNull(item.getByteBuffer("test")); - byte[] bytes = {1, 2, 3}; - item.withBinary("test", ByteBuffer.wrap(bytes)); - ByteBuffer toByteBuffer = item.getByteBuffer("test"); - assertTrue(Arrays.equals(bytes, toByteBuffer.array())); - assertTrue(Arrays.equals(bytes, item.getBinary("test"))); - item.withBinary("test", bytes); - assertSame(byte[].class, item.getTypeOf("test")); - assertTrue(Arrays.equals(bytes, item.getByteBuffer("test").array())); - } - - @Test(expected = IncompatibleTypeException.class) - public void toByteBuffer_IncompatibleTypeException() { - Item item = new Item(); - item.withString("test", "foo"); - item.getByteBuffer("test"); - } - - @Test - public void valToString() { - Item item = new Item(); - item.withNumber("test", BigDecimal.ONE); - assertEquals("1", item.getString("test")); - assertNull(item.getString("foo")); - item.withBoolean("test", false); - assertEquals("false", item.getString("test")); - } - - @Test - public void getStringSet_fromList() { - Item item = new Item(); - item.withList("test", "a", "b", "c"); - Set ss = item.getStringSet("test"); - assertTrue(ss.size() == 3); - assertTrue(ss.contains("a")); - assertTrue(ss.contains("b")); - assertTrue(ss.contains("c")); - } - - @Test - public void getStringSet_fromNumbers() { - Item item = new Item(); - item.withNumberSet("test", 1, 2); - Set ss = item.getStringSet("test"); - assertTrue(ss.size() == 2); - assertTrue(ss.contains("1")); - assertTrue(ss.contains("2")); - } - - @Test - public void getStringSet_fromBooleans() { - Item item = new Item(); - item.withList("test", true, false); - Set ss = item.getStringSet("test"); - assertTrue(ss.size() == 2); - assertTrue(ss.contains("true")); - assertTrue(ss.contains("false")); - } - - @Test - public void getStringSet_fromBoolean() { - Item item = new Item(); - item.withBoolean("test", true); - Set ss = item.getStringSet("test"); - assertTrue(ss.size() == 1); - assertTrue(ss.contains("true")); - } - - @Test(expected = IncompatibleTypeException.class) - public void getStringSet_fromBinary() { - Item item = new Item(); - item.withBinary("test", new byte[] {1, 2}); - item.getStringSet("test"); - } - - @Test - public void getStringSet_empty() { - Item item = new Item(); - item.with("test", new FluentArrayList()); - Set ss = item.getStringSet("test"); - assertTrue(ss.size() == 0); - } - - @Test(expected = IncompatibleTypeException.class) - public void getStringSet_duplicateElements() { - Item item = new Item(); - item.withList("test", "a", "b", "a"); - item.getStringSet("test"); - } - - @Test - public void getStringSet_nullElement() { - Item item = new Item(); - item.withList("test", "a", null, "c"); - Set ss = item.getStringSet("test"); - assertTrue(ss.size() == 3); - assertTrue(ss.contains("a")); - assertTrue(ss.contains(null)); - assertTrue(ss.contains("c")); - } - - @Test - public void getNumberSet() { - Item item = new Item(); - assertNull(item.getNumberSet("test")); - item.withList("test", BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN); - Set ss = item.getNumberSet("test"); - assertTrue(ss.size() == 3); - assertTrue(ss.contains(BigDecimal.ZERO)); - assertTrue(ss.contains(BigDecimal.ONE)); - assertTrue(ss.contains(BigDecimal.TEN)); - } - - @Test - public void getNumberSet_number() { - Item item = new Item(); - item.withNumber("test", 123); - Set ss = item.getNumberSet("test"); - assertTrue(ss.size() == 1); - assertTrue(ss.contains(new BigDecimal("123"))); - } - - @Test - public void getNumberSet_string() { - Item item = new Item(); - item.withString("test", "123"); - Set ss = item.getNumberSet("test"); - assertTrue(ss.size() == 1); - assertTrue(ss.contains(new BigDecimal("123"))); - } - - @Test - public void getNumberSet_empty() { - Item item = new Item(); - item.with("test", new FluentArrayList()); - Set ss = item.getNumberSet("test"); - assertTrue(ss.size() == 0); - } - - @Test(expected = IncompatibleTypeException.class) - public void getNumberSet_duplicateElements() { - Item item = new Item(); - item.withList("test", BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.ZERO); - item.getNumberSet("test"); - } - - @Test - public void getNumberSet_nullElement() { - Item item = new Item(); - item.withList("test", BigDecimal.ZERO, null, BigDecimal.TEN); - Set ss = item.getNumberSet("test"); - assertTrue(ss.size() == 3); - assertTrue(ss.contains(BigDecimal.ZERO)); - assertTrue(ss.contains(null)); - assertTrue(ss.contains(BigDecimal.TEN)); - } - - @Test - public void getBinarySet_bytes() { - Item item = new Item(); - assertNull(item.getBinarySet("test")); - item.withList("test", new byte[] {1, 2}, new byte[] {3, 4}); - Set bas = item.getBinarySet("test"); - assertTrue(bas.size() == 2); - boolean a = false; - boolean b = false; - for (byte[] ba : bas) { - if (Arrays.equals(ba, new byte[] {1, 2})) { - a = true; - } else if (Arrays.equals(ba, new byte[] {3, 4})) { - b = true; - } - } - assertTrue(a); - assertTrue(b); - } - - @Test - public void getBinarySet_singleByteArray() { - Item item = new Item(); - item.with("test", new byte[] {1, 2}); - Set bs = item.getBinarySet("test"); - assertTrue(bs.size() == 1); - boolean a = false; - for (byte[] ba : bs) { - if (Arrays.equals(ba, new byte[] {1, 2})) { - a = true; - } - } - assertTrue(a); - - Set bbs = item.getByteBufferSet("test"); - assertTrue(bbs.size() == 1); - a = false; - for (ByteBuffer ba : bbs) { - if (Arrays.equals(ba.array(), new byte[] {1, 2})) { - a = true; - } - } - assertTrue(a); - } - - @Test - public void getBinarySet_singleByteBuffer() { - Item item = new Item(); - item.with("test", ByteBuffer.wrap(new byte[] {1, 2})); - Set bbs = item.getByteBufferSet("test"); - assertTrue(bbs.size() == 1); - boolean a = false; - for (ByteBuffer ba : bbs) { - if (Arrays.equals(ba.array(), new byte[] {1, 2})) { - a = true; - } - } - assertTrue(a); - Set bs = item.getBinarySet("test"); - assertTrue(bs.size() == 1); - a = false; - for (byte[] ba : bs) { - if (Arrays.equals(ba, new byte[] {1, 2})) { - a = true; - } - } - assertTrue(a); - } - - @Test - public void getBinarySet_empty() { - Item item = new Item(); - item.with("test", new FluentHashSet()); - Set bs = item.getBinarySet("test"); - assertTrue(bs.size() == 0); - - Set bbs = item.getByteBufferSet("test"); - assertTrue(bbs.size() == 0); - } - - @Test(expected = IncompatibleTypeException.class) - public void getBinarySet_Incompatible() { - Item item = new Item(); - item.withString("test", "foo"); - item.getBinarySet("test"); - } - - @Test - public void getByteBufferSet_empty() { - Item item = new Item(); - assertNull(item.getByteBufferSet("test")); - item.with("test", new FluentHashSet()); - Set bs = item.getBinarySet("test"); - assertTrue(bs.size() == 0); - - Set bbs = item.getByteBufferSet("test"); - assertTrue(bbs.size() == 0); - } - - @Test(expected = IncompatibleTypeException.class) - public void getByteBufferSet_Incompatible() { - Item item = new Item(); - item.withString("test", "foo"); - item.getByteBufferSet("test"); - } - - @Test - public void getByteBufferSet() { - Item item = new Item(); - item.withList("test", new byte[] {1, 2, 3}, new byte[] {4, 5, 6}); - Set bs = item.getByteBufferSet("test"); - assertTrue(bs.size() == 2); - boolean a = false, b = false; - for (ByteBuffer bb : bs) { - if (Arrays.equals(bb.array(), new byte[] {1, 2, 3})) { - a = true; - } else if (Arrays.equals(bb.array(), new byte[] {4, 5, 6})) { - b = true; - } - } - assertTrue(a); - assertTrue(b); - } - - @Test - public void getList_null() { - Item item = new Item(); - assertNull(item.getList("test")); - } - - @Test - public void getList_list() { - Item item = new Item().withList("test", "abc", "def"); - List list = item.getList("test"); - assertTrue(list.size() == 2); - assertEquals("abc", list.get(0)); - assertEquals("def", list.get(1)); - } - - @Test - public void getList_string() { - Item item = new Item().withString("test", "foo"); - List list = item.getList("test"); - assertTrue(list.size() == 1); - assertEquals("foo", list.get(0)); - } - - @Test - public void toJSON_null() { - assertNull(new Item().getJson("test")); - assertNull(new Item().getJsonPretty("test")); - } - - @Test - public void fromJSON_null() { - assertNull(Item.fromJson(null)); - } - - @Test - public void fromJSON_array() { - Item item = new Item() - .withJson("arrayJson", "[\"foo\", \"bar\"]"); - List arrayJson = item.getList("arrayJson"); - String[] expectedArray = new String[] {"foo", "bar"}; - Assert.assertArrayEquals(expectedArray, arrayJson.toArray()); - } - - @Test - public void fromJSON_map() { - Item item = new Item() - .withJson("mapJson", "{\"foo\": \"bar\"}"); - Map mapJson = item.getMap("mapJson"); - Assert.assertEquals("bar", mapJson.get("foo")); - } - - @Test - public void toFromJSON() { - Item item = new Item() - .withString("stringA", "stringV") - .withFloat("floatA", 123.45f) - // Jackson will convert byte[] into Base64-encoded binary data - .withBinary("binaryA", new byte[] {1, 2, 3}) - .withBoolean("booleanA", true) - .withNull("nullA") - .withJson("jsonA", "{\"myjson\": 321}") - .withList("listA", "a", "b", "c") - .withMap("mapA", new ValueMap().with("map-a", "a").with("map-b", "b")) - .withStringSet("strSetA", "sa", "sb", "sc") - .withNumberSet("numSetA", BigDecimal.ONE, BigDecimal.ZERO) - .withBinarySet("binarySetA", new byte[] {00, 11}, new byte[] {22, 33}) - .withBinarySet("byteBufferSetA", - ByteBuffer.wrap(new byte[] {44, 55}), - ByteBuffer.wrap(new byte[] {66, 77})); - String json = item.toJsonPretty(); - System.out.println(json); - System.out.println("byte[]{1,2,3} => " + BinaryUtils.toBase64(new byte[] {1, 2, 3})); - System.out.println("byte[]{00,11} => " + BinaryUtils.toBase64(new byte[] {00, 11})); - System.out.println("byte[]{22,33} => " + BinaryUtils.toBase64(new byte[] {22, 33})); - System.out.println("byte[]{44,44} => " + BinaryUtils.toBase64(new byte[] {44, 55})); - System.out.println("byte[]{66,77} => " + BinaryUtils.toBase64(new byte[] {66, 77})); - Item itemTo = Item.fromJson(json); - System.out.println(itemTo); - assertTrue(List.class.isAssignableFrom(itemTo.getTypeOf("binarySetA"))); - assertTrue(List.class.isAssignableFrom(itemTo.getTypeOf("byteBufferSetA"))); - itemTo.base64Decode("binaryA", "binarySetA", "byteBufferSetA"); - assertTrue(Arrays.equals(itemTo.getBinary("binaryA"), item.getBinary("binaryA"))); - assertTrue(itemTo.getBinarySet("binarySetA").size() == 2); - { // verity the binary content of "binarySetA" - boolean a = false, b = false; - for (byte[] bytes : itemTo.getBinarySet("binarySetA")) { - if (Arrays.equals(bytes, new byte[] {00, 11})) { - a = true; - } else if (Arrays.equals(bytes, new byte[] {22, 33})) { - b = true; - } - } - assertTrue(a); - assertTrue(b); - assertTrue(Set.class.isAssignableFrom(itemTo.getTypeOf("binarySetA"))); - } - assertTrue(itemTo.getBinarySet("byteBufferSetA").size() == 2); - { // verity the binary content of "byteBufferSetA" - boolean a = false, b = false; - for (byte[] bytes : itemTo.getBinarySet("byteBufferSetA")) { - if (Arrays.equals(bytes, new byte[] {44, 55})) { - a = true; - } else if (Arrays.equals(bytes, new byte[] {66, 77})) { - b = true; - } - } - assertTrue(a); - assertTrue(b); - assertTrue(Set.class.isAssignableFrom(itemTo.getTypeOf("byteBufferSetA"))); - } - // JSON doesn't support Set, so all all sets now become lists - assertTrue(List.class.isAssignableFrom(itemTo.getTypeOf("strSetA"))); - assertTrue(List.class.isAssignableFrom(itemTo.getTypeOf("numSetA"))); - itemTo.convertListsToSets("strSetA", "numSetA"); - assertTrue(Set.class.isAssignableFrom(itemTo.getTypeOf("strSetA"))); - assertTrue(Set.class.isAssignableFrom(itemTo.getTypeOf("numSetA"))); - { - Set set = itemTo.getStringSet("strSetA"); - assertTrue(set.size() == item.getStringSet("strSetA").size()); - set.containsAll(item.getStringSet("strSetA")); - } - { - Set set = itemTo.getNumberSet("numSetA"); - assertTrue(set.size() == item.getStringSet("numSetA").size()); - set.containsAll(item.getNumberSet("numSetA")); - } - } - - @Test(expected = IllegalArgumentException.class) - public void withStringSet_duplicates() { - new Item().withStringSet("test", "a", "b", "a"); - } - - @Test(expected = IllegalArgumentException.class) - public void withBigDecimalSet_duplicates() { - new Item().withBigDecimalSet("test", new BigDecimal("1"), BigDecimal.ONE); - } - - @Test(expected = IllegalArgumentException.class) - public void withNumberSet_duplicates() { - new Item().withNumberSet("test", new BigDecimal("1"), new BigInteger("1")); - } - - @Test(expected = IllegalArgumentException.class) - public void withNumberSet_duplicates2() { - new Item().withNumberSet("test", new BigDecimal("1.0"), new Float("1")); - } - - @Test(expected = IllegalArgumentException.class) - public void withNumberSet_duplicates3() { - Set set = new FluentHashSet().withAll( - new BigDecimal("1.0"), new Float("1")); - assertTrue(set.size() == 2); - // Become duplicates when get converted into BigDecimal - new Item().withNumberSet("test", set); - } - - @Test(expected = IllegalArgumentException.class) - public void invalidNullInput() { - new Item().withNumber("test", null); - } - - @Test(expected = IllegalArgumentException.class) - public void nullAttrName() { - new Item().withNull(null); - } - - @Test(expected = IllegalArgumentException.class) - public void blankAttrName() { - new Item().withNull(" "); - } - - @Test(expected = IllegalArgumentException.class) - public void withKeyComponents_null() { - new Item().withKeyComponents(); - } - - @Test(expected = IllegalArgumentException.class) - public void withKeyComponents_nullComponent() { - new Item().withKeyComponents((KeyAttribute) null); - } - - @Test - public void withKeyComponents() { - Item item = new Item().withKeyComponents(new KeyAttribute("name", 123)); - Assert.assertTrue(123 == item.getInt("name")); - Assert.assertTrue(BigDecimal.class == item.getTypeOf("name")); - } - - @Test(expected = IncompatibleTypeException.class) - public void getBOOL_null() { - Item item = new Item(); - item.getBool("test"); - } - - @Test(expected = IllegalArgumentException.class) - public void withPrimaryKey_null() { - new Item().withPrimaryKey(null); - } - - @Test(expected = IllegalArgumentException.class) - public void withPrimaryKey_empty() { - new Item().withPrimaryKey(new PrimaryKey()); - } - - @Test(expected = IncompatibleTypeException.class) - public void getBOOL_invalidValue() { - Item item = new Item().withInt("test", 123); - item.getBool("test"); - } - - @Test - public void getBOOL_Boolean() { - Item item = new Item().withBoolean("test", Boolean.TRUE); - assertEquals(Boolean.TRUE, item.getBool("test")); - item.withBoolean("test", Boolean.FALSE); - assertEquals(Boolean.FALSE, item.getBool("test")); - } - - @Test - public void getBOOL_01() { - Item item = new Item().withString("test", "1"); - assertEquals(Boolean.TRUE, item.getBool("test")); - item.withString("test", "0"); - assertEquals(Boolean.FALSE, item.getBool("test")); - item.withString("test", "true"); - assertEquals(Boolean.TRUE, item.getBool("test")); - item.withString("test", "false"); - assertEquals(Boolean.FALSE, item.getBool("test")); - } - - @Test - public void withShort() { - assertTrue(1 == new Item().withShort("test", (short) 1).getInt("test")); - } - - @Test(expected = IllegalArgumentException.class) - public void withShort_emptyName() { - new Item().withShort(" ", (short) 1); - } - - @Test(expected = IllegalArgumentException.class) - public void withShort_nullName() { - new Item().withShort(null, (short) 1); - } - - @Test - public void withDouble() { - assertTrue(1 == new Item().withDouble("test", 1.0).getInt("test")); - } - - @Test(expected = IllegalArgumentException.class) - public void withDouble_emptyName() { - new Item().withDouble(" ", 1.0); - } - - @Test(expected = IllegalArgumentException.class) - public void withDouble_nullName() { - new Item().withDouble(null, 1.0); - } - - // https://github.com/aws/aws-sdk-java/issues/311#issuecomment-64474230 - @Test(expected = ClassCastException.class) - public void issues311() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMap("item_key"); - @SuppressWarnings("unused") - BigInteger b = mapout.get("map_key"); - } - - @Test - public void getRawMap() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getRawMap("item_key"); - Object b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_BigInteger() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", BigInteger.class); - BigInteger b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_BigDecimal() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", BigDecimal.class); - BigDecimal b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_Short() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", Short.class); - Short b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_Integer() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", Integer.class); - Integer b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_Long() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", Long.class); - Long b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_Float() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", Float.class); - Float b = mapout.get("map_key"); - assertEquals(b.toString(), "123.0", b.toString()); - } - - @Test - public void getMapOfNumbers_Double() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", Double.class); - Double b = mapout.get("map_key"); - assertEquals(b.toString(), "123.0", b.toString()); - } - - @Test - public void getMapOfNumbers_Number() { - Map bigIntMap_input = new HashMap(); - bigIntMap_input.put("map_key", new BigInteger("123")); - - Item i = new Item().withMap("item_key", bigIntMap_input); - Map mapout = i.getMapOfNumbers("item_key", Number.class); - Number b = mapout.get("map_key"); - assertEquals("123", b.toString()); - } - - @Test - public void getMapOfNumbers_NotExist() { - Item i = new Item(); - assertNull(i.getMapOfNumbers("item_key", Short.class)); - } - - @Test - public void getBigInteger() { - Item i = new Item().withInt("item_key", 123); - BigInteger b = i.getBigInteger("item_key"); - assertEquals("123", b.toString()); - - assertNull(i.getBigInteger("foo")); - } - - @Test - public void getShort() { - Item i = new Item().withInt("item_key", 123); - short b = i.getShort("item_key"); - assertTrue(b == 123); - } - - @Test(expected = NumberFormatException.class) - public void getShortNotExist() { - Item i = new Item(); - i.getShort("item_key"); - } - - @Test - public void getFloat() { - Item i = new Item().withFloat("item_key", 123.45f); - float b = i.getFloat("item_key"); - assertTrue(b == 123.45f); - } - - @Test(expected = NumberFormatException.class) - public void getFloatNotExist() { - Item i = new Item(); - i.getFloat("item_key"); - } - - @Test - public void getDouble() { - Item i = new Item().withDouble("item_key", 123.45); - double b = i.getFloat("item_key"); - assertTrue(b + "", b > 123.44 && b <= 123.45); - } - - @Test(expected = NumberFormatException.class) - public void getDoubleNotExist() { - Item i = new Item(); - i.getDouble("item_key"); - } - - @Test(expected = IllegalArgumentException.class) - public void withNullBigInteger() { - new Item().withBigInteger("foo", null); - } - - @Test(expected = IllegalArgumentException.class) - public void withNullNumber() { - new Item().withNumber("foo", null); - } - - @Test - public void hasAttribute() { - assertFalse(new Item().hasAttribute("foo")); - assertTrue(new Item().with("foo", null).hasAttribute("foo")); - assertTrue(new Item().with("foo", "fooval").hasAttribute("foo")); - assertTrue(new Item().with("foo", "fooval").with("bar", "barval").hasAttribute("foo")); - assertTrue(new Item().with("foo", "fooval").with("bar", "barval").hasAttribute("bar")); - assertFalse(new Item().with("foo", "fooval").with("bar", "barval").hasAttribute("notExist")); - } - - @Test - public void testEquals() { - assertEquals(new Item().with("foo", "fooval").with("bar", "barval"), - new Item().with("foo", "fooval").with("bar", "barval")); - assertEquals(new Item().with("foo", "fooval").with("bar", "barval"), - new Item().withPrimaryKey(new PrimaryKey("foo", "fooval", "bar", "barval"))); - - assertFalse(new Item().equals(new Object())); - assertFalse(new Item().equals(null)); - - Set items = new HashSet(); - items.add(new Item().with("foo", "fooval").with("bar", "barval")); - items.add(new Item().with("foo", "fooval")); - assertTrue(items.size() == 2); - - assertTrue(items.contains(new Item().with("foo", "fooval"))); - assertTrue(items.contains(new Item().with("foo", "fooval").with("bar", "barval"))); - assertFalse(items.contains(new Item())); - - items.add(new Item()); - assertTrue(items.contains(new Item())); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemTestUtils.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemTestUtils.java deleted file mode 100644 index e0a3e73f3b71..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ItemTestUtils.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - -public class ItemTestUtils { - /** - * Used for testing purposes. - */ - public static boolean equalsItem(Item itemFrom, Item itemTo) { - return equalsSimpleValue(itemFrom.asMap(), itemTo.asMap()); - } - - /** - * Used for testing purposes. - */ - @SuppressWarnings("unchecked") - public static boolean equalsSimpleValue(Object v0, Object v1) { - if (v0 == null || v1 == null) { - return v0 == null && v1 == null; - } - // Byte buffer or byte array - if (v0 instanceof ByteBuffer) { - return equalsByteBuffer((ByteBuffer) v0, v1); - } else if (v1 instanceof ByteBuffer) { - return equalsByteBuffer((ByteBuffer) v1, v0); - } else if (v0 instanceof byte[]) { - return equalsByteArray((byte[]) v0, v1); - } else if (v1 instanceof byte[]) { - return equalsByteArray((byte[]) v1, v0); - } - // Number - if (v0 instanceof Number && v1 instanceof Number) { - String s0 = InternalUtils.valToString(v0); - String s1 = InternalUtils.valToString(v1); - return s0.equals(s1); - } - // Map - if (v0 instanceof Map && v1 instanceof Map) { - Map map0 = (Map) v0; - Map map1 = (Map) v1; - - if (map0.size() != map1.size()) { - return false; - } - - for (Entry e : map0.entrySet()) { - if (!equalsSimpleValue( - e.getValue(), - map1.get(e.getKey()))) { - return false; - } - } - return true; - } - if (v0 instanceof List && v1 instanceof List) { - List map0 = (List) v0; - List map1 = (List) v1; - - if (map0.size() != map1.size()) { - return false; - } - for (int i = 0; i < map0.size(); i++) { - if (!equalsSimpleValue(map0.get(i), map1.get(i))) { - return false; - } - } - return true; - } - // Set - // Currently this works only if both set have the elements in the same - // iteration order. Can we do better ? - if (v0 instanceof Set && v1 instanceof Set) { - Set set0 = (Set) v0; - Set set1 = (Set) v1; - - if (set0.size() != set1.size()) { - return false; - } - - for (Object element0 : set0) { - boolean matchFound = false; - - for (Object element1 : set1) { - if (equalsSimpleValue(element0, element1)) { - matchFound = true; - break; - } - } - - if (!matchFound) { - return false; - } - } - - return true; - } - - return v0.equals(v1); - } - - /** - * Used for testing purposes. - */ - private static boolean equalsByteBuffer(ByteBuffer fromByteBuffer, Object o) { - byte[] from = fromByteBuffer.array(); - return equalsByteArray(from, o); - } - - /** - * Used for testing purposes. - */ - private static boolean equalsByteArray(byte[] from, Object o) { - if (o instanceof ByteBuffer) { - byte[] to = ((ByteBuffer) o).array(); - return Arrays.equals(from, to); - } else { - return (o instanceof byte[]) - && Arrays.equals(from, ((byte[]) o)); - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/KeyAttribute.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/KeyAttribute.java deleted file mode 100644 index b55993a6337d..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/KeyAttribute.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - -/** - * A key attribute which consists of an attribute name and value. - */ -public class KeyAttribute extends Attribute { - - /** - * A key attribute which consists of an attribute name and value. - */ - public KeyAttribute(String attrName, Object value) { - super(attrName, value); - InternalUtils.checkInvalidAttrName(attrName); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/KeyCondition.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/KeyCondition.java deleted file mode 100644 index e85bb813ba4d..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/KeyCondition.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; - -/** - * Simple key conditions. - */ -public enum KeyCondition { - EQ(ComparisonOperator.EQ), - LE(ComparisonOperator.LE), - LT(ComparisonOperator.LT), - GE(ComparisonOperator.GE), - GT(ComparisonOperator.GT), - BEGINS_WITH(ComparisonOperator.BEGINS_WITH), - BETWEEN(ComparisonOperator.BETWEEN),; - - private final ComparisonOperator comparisonOperator; - - KeyCondition(ComparisonOperator comparisonOperator) { - this.comparisonOperator = comparisonOperator; - } - - public ComparisonOperator toComparisonOperator() { - return comparisonOperator; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/LowLevelResultListener.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/LowLevelResultListener.java deleted file mode 100644 index d628b310ea4c..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/LowLevelResultListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -/** - * Can be used to listen to the event of receiving a low level result of type R - * from the server side. - * - * @param - * low level result type - */ -public abstract class LowLevelResultListener { - public static final LowLevelResultListener NONE = - new LowLevelResultListener() { - @Override - public void onLowLevelResult(Object result) { - } - }; - - @SuppressWarnings("unchecked") - public static LowLevelResultListener none() { - return (LowLevelResultListener) NONE; - } - - public abstract void onLowLevelResult(R lowLevelResult); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Page.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Page.java deleted file mode 100644 index 7c5d5d938503..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Page.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Iterator; -import java.util.List; -import java.util.NoSuchElementException; -import software.amazon.awssdk.core.exception.SdkServiceException; - -/** - * A page contains a list of items; accessing methods on the list are - * guaranteed to be purely in-memory operations that will not block or throw - * exceptions because of transient network issues. A page also knows whether it - * has a "next page", and if so knows how to retrieve it (which - * will almost certainly involve a remote network call that may block or - * fail). - * - * @param item type - * @param low level result type - */ -public abstract class Page implements Iterable { - private final List content; - private final R lowLevelResult; - - /** - * @param content an unmodifiable list of content - * @param lowLevelResult the low level (response) result from AWS - */ - public Page(List content, R lowLevelResult) { - if (content == null || lowLevelResult == null) { - throw new IllegalArgumentException("both content and lowLevelResult must be specified"); - } - this.content = content; - this.lowLevelResult = lowLevelResult; - } - - /** - * Checks whether this page has a "next page." If this method returns - * true, the next page can be retrieved by calling {@code next}. If it - * returns false, any call to {@code next} will be guaranteed to throw an - * {@code IllegalStateException}. - * - * @return true if there is next page; false otherwise - */ - public abstract boolean hasNextPage(); - - /** - * Retrieves the next page. - * - * @return the next page - * @throws NoSuchElementException if there is no next page - * @throws SdkServiceException on error making the remote call - */ - public abstract Page nextPage(); - - public final int size() { - return content.size(); - } - - @Override - public final Iterator iterator() { - return content.iterator(); - } - - @Override - public String toString() { - return content.toString(); - } - - public final R lowLevelResult() { - return lowLevelResult; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PageTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PageTest.java deleted file mode 100644 index 517c7906b787..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PageTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.ArrayList; -import java.util.List; -import org.junit.Test; - -public class PageTest { - - @Test(expected = IllegalArgumentException.class) - public void testNull_content() { - new TestPage(null, new Object()); - } - - ; - - @Test(expected = IllegalArgumentException.class) - public void testNull_result() { - new TestPage(new ArrayList(), null); - } - - @Test - public void test_toString() { - System.out.println(new TestPage(new ArrayList(), new Object()) - .toString()); - } - - private static class TestPage extends Page { - TestPage(List content, Object result) { - super(content, result); - } - - ; - - @Override - public boolean hasNextPage() { - return false; - } - - @Override - public TestPage nextPage() { - return null; - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PrimaryKey.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PrimaryKey.java deleted file mode 100644 index ad4236ee5283..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PrimaryKey.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - -/** - * Used to represent a primary key that has one or multiple key components. - */ -public class PrimaryKey { - private final Map components = new LinkedHashMap(); - - public PrimaryKey() { - } - - /** - * Constructs with the specified key components. - */ - public PrimaryKey(KeyAttribute... components) { - addComponents(components); - } - - /** - * Constructs with a hash key. - */ - public PrimaryKey(String hashKeyName, Object hashKeyValue) { - addComponent(hashKeyName, hashKeyValue); - } - - /** - * Constructs with a hash key and a range key. - */ - public PrimaryKey(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - if (hashKeyName.equals(rangeKeyName)) { - throw new IllegalArgumentException("hashKyeName must not be the same as the rangeKeyName"); - } - addComponent(hashKeyName, hashKeyValue); - addComponent(rangeKeyName, rangeKeyValue); - } - - /** - * Returns all the key components of this primary key. - */ - public Collection getComponents() { - return components.values(); - } - - /** - * Returns all the key component names of this primary key as a set. - */ - public Set getComponentNameSet() { - return components.keySet(); - } - - /** - * Returns true if this primary has the specified key attribute name; - * false otherwise. - */ - public boolean hasComponent(String attrName) { - return components.containsKey(attrName); - } - - /** - * Add one or multiple key components to this primary key. - * - * Note adding a key component with the same name as that of an existing - * one would overwrite and become a single key component instead of two. - */ - public PrimaryKey addComponents(KeyAttribute... components) { - if (components != null) { - for (KeyAttribute ka : components) { - InternalUtils.rejectNullInput(ka); - this.components.put(ka.name(), ka); - } - } - return this; - } - - /** - * Add a key component to this primary key. - * - * Note adding a key component with the same name as that of an existing - * one would overwrite and become a single key component instead of two. - */ - public PrimaryKey addComponent(String keyAttributeName, Object keyAttributeValue) { - components.put(keyAttributeName, - new KeyAttribute(keyAttributeName, keyAttributeValue)); - return this; - } - - @Override - public String toString() { - return String.valueOf(components); - } - - @Override - public int hashCode() { - return components.hashCode(); - } - - @Override - public boolean equals(Object in) { - if (in instanceof PrimaryKey) { - PrimaryKey that = (PrimaryKey) in; - return this.components.equals(that.components); - } else { - return false; - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PrimaryKeyTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PrimaryKeyTest.java deleted file mode 100644 index dea5cbce36c9..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PrimaryKeyTest.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.HashSet; -import java.util.Set; -import org.junit.Test; - -public class PrimaryKeyTest { - - @Test - public void ctor_KeyAttributes() { - new PrimaryKey(); - } - - @Test(expected = IllegalArgumentException.class) - public void ctor_nullKeyAttributes() { - new PrimaryKey((KeyAttribute) null); - } - - @Test - public void ctor_nullValue() { - new PrimaryKey("name", null); - } - - @Test(expected = IllegalArgumentException.class) - public void ctor_nullName() { - new PrimaryKey(null, "val"); - } - - @Test(expected = IllegalArgumentException.class) - public void ctor_emptyName() { - new PrimaryKey(" ", "val"); - } - - @Test(expected = IllegalArgumentException.class) - public void ctor_sameHashRangeKeyNames() { - new PrimaryKey("key", "val1", "key", "val2"); - } - - @Test(expected = IllegalArgumentException.class) - public void ctor_badHashKeyName() { - new PrimaryKey("", "val1", "key", "val2"); - } - - @Test(expected = IllegalArgumentException.class) - public void ctor_badRangeKeyName() { - new PrimaryKey("key1", "val1", "", "val2"); - } - - @Test(expected = IllegalArgumentException.class) - public void addComponents_nullElement() { - new PrimaryKey().addComponents((KeyAttribute) null); - } - - @Test(expected = IllegalArgumentException.class) - public void addComponent_nullName() { - new PrimaryKey().addComponent(null, "val"); - } - - @Test(expected = IllegalArgumentException.class) - public void addComponent_emptyName() { - new PrimaryKey().addComponent(" ", "val"); - } - - @Test - public void addComponent_nullVal() { - new PrimaryKey().addComponent("key", null); - } - - @Test - public void addComponents_null() { - new PrimaryKey().addComponents((KeyAttribute[]) null); - } - - @Test - public void ctor_nullHashRangeKeys() { - new PrimaryKey("hashkey", null, "rangekey", null); - } - - @Test - public void testEquals() { - assertEquals(new PrimaryKey("hashkey", null, "rangekey", null), - new PrimaryKey("hashkey", null, "rangekey", null)); - - assertEquals(new PrimaryKey("k1", "v1", "k2", "v2"), - new PrimaryKey("k1", "v1", "k2", "v2")); - assertFalse(new PrimaryKey("k1", "v1").equals(new Attribute("k1", "v1"))); - assertFalse(new PrimaryKey("k1", "v1").equals(null)); - - Set set = new HashSet(); - set.add(new PrimaryKey("k1", "v1", "k2", "v2")); - set.add(new PrimaryKey("k1", "v1", "k2", "v2")); - assertTrue(set.size() == 1); - - set.add(new PrimaryKey("k1", "v1")); - assertTrue(set.size() == 2); - } - - @Test - public void hasComponent() { - assertTrue(new PrimaryKey("hashkey", null, "rangekey", null).hasComponent("hashkey")); - assertTrue(new PrimaryKey("hashkey", null, "rangekey", null).hasComponent("rangekey")); - assertFalse(new PrimaryKey("hashkey", null, "rangekey", null).hasComponent("notExist")); - assertTrue(new PrimaryKey("k1", "v1", "k2", "v2").hasComponent("k1")); - assertTrue(new PrimaryKey("k1", "v1", "k2", "v2").hasComponent("k2")); - assertFalse(new PrimaryKey("k1", "v1", "k2", "v2").hasComponent("notExist")); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PutItemOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PutItemOutcome.java deleted file mode 100644 index e13067f7a945..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/PutItemOutcome.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; - -/** - * The outcome of putting an item to a DynamoDB table. - */ -public class PutItemOutcome { - private final PutItemResponse result; - - /** - * @param result the low-level result; must not be null - */ - public PutItemOutcome(PutItemResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns all the returned attributes as a (non-null) {@link Item}. - */ - public Item getItem() { - Map attributes = - InternalUtils.toSimpleMapValue(result.attributes()); - Item item = Item.fromMap(attributes); - return item; - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public PutItemResponse getPutItemResponse() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/QueryFilter.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/QueryFilter.java deleted file mode 100644 index 920efe49eecc..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/QueryFilter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.Filter; - -/** - * A query filter. - * - * Typical usages: - *
    - * new QueryFilter("strAttr").eq("attrValue"); - *

    - * new QueryFilter("intAttr").gt(42); - *

    - * ... - *

    - */ -public class QueryFilter extends Filter { - - /** - * A query filter. - * - * Typical usages: - *
    - * new QueryFilter("strAttr").eq("attrValue"); - *

    - * new QueryFilter("intAttr").gt(42); - *

    - * ... - *

    - */ - public QueryFilter(String attr) { - super(attr); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/QueryOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/QueryOutcome.java deleted file mode 100644 index 7f0176996bf0..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/QueryOutcome.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.List; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - -/** - * The outcome of query on DynamoDB table. - */ -public class QueryOutcome { - private final QueryResponse result; - - /** - * @param result the low-level result; must not be null - */ - public QueryOutcome(QueryResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns a non-null list of the returned items; can be empty. - */ - public List getItems() { - return InternalUtils.toItemList(result.items()); - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public QueryResponse getQueryResponse() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/RangeKeyCondition.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/RangeKeyCondition.java deleted file mode 100644 index 81c1208ef05d..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/RangeKeyCondition.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - - -/** - * A condition for selecting items with a range key. Typical usages: - *
    - * new RangeKeyCondition("strAttr").eq("attrValue"); - *

    - * new RangeKeyCondition("intAttr").gt(42); - *

    - * ... - *

    - */ -public class RangeKeyCondition { - private final String attrName; - private KeyCondition kcond; - private Object[] values; - - /** - * A condition for selecting items with a range key. Typical usages: - *
    - * new RangeKeyCondition("strAttr").eq("attrValue"); - *

    - * new RangeKeyCondition("intAttr").gt(42); - *

    - * ... - *

    - */ - public RangeKeyCondition(String attrName) { - InternalUtils.checkInvalidAttrName(attrName); - this.attrName = attrName; - } - - public String getAttrName() { - return attrName; - } - - public KeyCondition getKeyCondition() { - return kcond; - } - - public Object[] values() { - return values == null ? null : values.clone(); - } - - /** - * Creates and returns a condition of the range key being equal to the given - * value. - */ - public RangeKeyCondition eq(Object val) { - kcond = KeyCondition.EQ; - return values(val); - } - - /** - * Creates and returns a condition of the range key with a value that begins - * with the given value. - */ - public RangeKeyCondition beginsWith(String val) { - kcond = KeyCondition.BEGINS_WITH; - return values(val); - } - - /** - * Creates and returns a condition of the range key that has a value between - * the given values. - */ - public RangeKeyCondition between(Object low, Object hi) { - kcond = KeyCondition.BETWEEN; - return values(low, hi); - } - - /** - * Creates and returns a condition of the range key being greater than or - * equal to the given value. - */ - public RangeKeyCondition ge(Object val) { - kcond = KeyCondition.GE; - return values(val); - } - - /** - * Creates and returns a condition of the range key being greater than the - * given value. - */ - public RangeKeyCondition gt(Object val) { - kcond = KeyCondition.GT; - return values(val); - } - - /** - * Creates and returns a condition of the range key being less than or equal - * to the given value. - */ - public RangeKeyCondition le(Object val) { - kcond = KeyCondition.LE; - return values(val); - } - - /** - * Creates and returns a condition of the range key being less than the - * given value. - */ - public RangeKeyCondition lt(Object val) { - kcond = KeyCondition.LT; - return values(val); - } - - private RangeKeyCondition values(Object... values) { - this.values = values; - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ScanFilter.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ScanFilter.java deleted file mode 100644 index 99753f842374..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ScanFilter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.Filter; - -/** - * A scan filter. - * - * Typical usages: - *
    - * new ScanFilter("strAttr").eq("attrValue"); - *

    - * new ScanFilter("intAttr").gt(42); - *

    - * ... - *

    - */ -public class ScanFilter extends Filter { - - /** - * A scan filter. - * - * Typical usages: - *
    - * new ScanFilter("strAttr").eq("attrValue"); - *

    - * new ScanFilter("intAttr").gt(42); - *

    - * ... - *

    - */ - public ScanFilter(String attr) { - super(attr); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ScanOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ScanOutcome.java deleted file mode 100644 index 834e46a7a7f9..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/ScanOutcome.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.List; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -/** - * The outcome of scanning the DynamoDB table. - */ -public class ScanOutcome { - private final ScanResponse result; - - /** - * @param result the low-level result; must not be null - */ - public ScanOutcome(ScanResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns a non-null list of the returned items; can be empty. - */ - public List getItems() { - return InternalUtils.toItemList(result.items()); - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public ScanResponse scanResult() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Table.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Table.java deleted file mode 100644 index 7761cf4d0c89..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/Table.java +++ /dev/null @@ -1,776 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.api.DeleteItemApi; -import software.amazon.awssdk.services.dynamodb.document.api.GetItemApi; -import software.amazon.awssdk.services.dynamodb.document.api.PutItemApi; -import software.amazon.awssdk.services.dynamodb.document.api.QueryApi; -import software.amazon.awssdk.services.dynamodb.document.api.ScanApi; -import software.amazon.awssdk.services.dynamodb.document.api.UpdateItemApi; -import software.amazon.awssdk.services.dynamodb.document.internal.DeleteItemImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.GetItemImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.document.internal.PutItemImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.QueryImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.ScanImpl; -import software.amazon.awssdk.services.dynamodb.document.internal.UpdateItemImpl; -import software.amazon.awssdk.services.dynamodb.document.spec.DeleteItemSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.GetItemSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.PutItemSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.UpdateItemSpec; -import software.amazon.awssdk.services.dynamodb.document.spec.UpdateTableSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateGlobalSecondaryIndexAction; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableResponse; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexDescription; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexUpdate; -import software.amazon.awssdk.services.dynamodb.model.IndexStatus; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; -import software.amazon.awssdk.services.dynamodb.model.UpdateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.UpdateTableResponse; - -/** - * A DynamoDB table. Instance of this class is typically obtained via - * {@link DynamoDb#getTable(String)}. - */ -@ThreadSafe -public class Table implements PutItemApi, GetItemApi, QueryApi, ScanApi, - UpdateItemApi, DeleteItemApi { - private static final long SLEEP_TIME_MILLIS = 5000; - private final String tableName; - private final DynamoDbClient client; - private final PutItemImpl putItemDelegate; - private final GetItemImpl getItemDelegate; - private final UpdateItemImpl updateItemDelegate; - private final DeleteItemImpl deleteItemDelegate; - private final QueryImpl queryDelegate; - private final ScanImpl scanDelegate; - private volatile TableDescription tableDescription; - - public Table(DynamoDbClient client, String tableName) { - this(client, tableName, null); - } - - public Table(DynamoDbClient client, String tableName, - TableDescription tableDescription) { - if (client == null) { - throw new IllegalArgumentException("client must be specified"); - } - if (tableName == null || tableName.trim().length() == 0) { - throw new IllegalArgumentException("table name must not be null or empty"); - } - this.client = client; - this.tableName = tableName; - this.tableDescription = tableDescription; - - this.putItemDelegate = new PutItemImpl(client, this); - this.getItemDelegate = new GetItemImpl(client, this); - this.updateItemDelegate = new UpdateItemImpl(client, this); - this.deleteItemDelegate = new DeleteItemImpl(client, this); - - this.queryDelegate = new QueryImpl(client, this); - this.scanDelegate = new ScanImpl(client, this); - } - - public String getTableName() { - return tableName; - } - - /** - * Returns the table description; or null if the table description has not - * yet been described via {@link #describe()}. No network call. - */ - public TableDescription getDescription() { - return tableDescription; - } - - /** - * Retrieves the table description from DynamoDB. Involves network calls. - * Meant to be called as infrequently as possible to avoid throttling - * exception from the server side. - * - * @return a non-null table description - * - * @throws ResourceNotFoundException if the table doesn't exist - */ - public TableDescription describe() { - DescribeTableResponse result = client.describeTable( - InternalUtils.applyUserAgent(DescribeTableRequest.builder().tableName(tableName).build())); - TableDescription description = result.table(); - tableDescription = description; - return description; - } - - /** - * Gets a reference to the specified index. No network call. - */ - public Index getIndex(String indexName) { - return new Index(client, indexName, this); - } - - @Override - public PutItemOutcome putItem(Item item) { - return putItemDelegate.putItem(item); - } - - @Override - public PutItemOutcome putItem(Item item, Expected... expected) { - return putItemDelegate.putItem(item, expected); - } - - @Override - public PutItemOutcome putItem(Item item, String conditionExpression, - Map nameMap, Map valueMap) { - return putItemDelegate.putItem(item, conditionExpression, nameMap, - valueMap); - } - - @Override - public PutItemOutcome putItem(PutItemSpec spec) { - return putItemDelegate.putItem(spec); - } - - @Override - public GetItemOutcome getItemOutcome(KeyAttribute... primaryKeyComponents) { - return getItemDelegate.getItemOutcome(primaryKeyComponents); - } - - @Override - public GetItemOutcome getItemOutcome(PrimaryKey primaryKey) { - return getItemDelegate.getItemOutcome(primaryKey); - } - - @Override - public GetItemOutcome getItemOutcome(PrimaryKey primaryKey, - String projectionExpression, Map nameMap) { - return getItemDelegate.getItemOutcome(primaryKey, projectionExpression, - nameMap); - } - - @Override - public GetItemOutcome getItemOutcome(GetItemSpec params) { - return getItemDelegate.getItemOutcome(params); - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - AttributeUpdate... attributeUpdates) { - return updateItemDelegate.updateItem(primaryKey, attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - Collection expected, AttributeUpdate... attributeUpdates) { - return updateItemDelegate.updateItem(primaryKey, expected, - attributeUpdates); - - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - String updateExpression, Map nameMap, - Map valueMap) { - return updateItemDelegate.updateItem(primaryKey, updateExpression, - nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap) { - return updateItemDelegate.updateItem(primaryKey, updateExpression, - conditionExpression, nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(UpdateItemSpec updateItemSpec) { - return updateItemDelegate.updateItem(updateItemSpec); - } - - @Override - public ItemCollection query(String hashKeyName, Object hashKeyValue) { - return queryDelegate.query(hashKeyName, hashKeyValue); - } - - @Override - public ItemCollection query(KeyAttribute hashKey) { - return queryDelegate.query(hashKey); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition) { - return queryDelegate.query(hashKey, rangeKeyCondition); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, String filterExpression, - String projectionExpression, Map nameMap, - Map valueMap) { - return queryDelegate.query(hashKey, rangeKeyCondition, - filterExpression, projectionExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, QueryFilter... queryFilters) { - return queryDelegate.query(hashKey, rangeKeyCondition, queryFilters); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, String filterExpression, - Map nameMap, Map valueMap) { - return queryDelegate.query(hashKey, rangeKeyCondition, - filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(QuerySpec spec) { - return queryDelegate.query(spec); - } - - @Override - public ItemCollection scan(ScanFilter... scanFilters) { - return scanDelegate.scan(scanFilters); - } - - @Override - public ItemCollection scan(String filterExpression, - Map nameMap, Map valueMap) { - return scanDelegate.scan(filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection scan(String filterExpression, - String projectionExpression, Map nameMap, - Map valueMap) { - return scanDelegate.scan(filterExpression, projectionExpression, nameMap, valueMap); - } - - @Override - public ItemCollection scan(ScanSpec params) { - return scanDelegate.scan(params); - } - - @Override - public DeleteItemOutcome deleteItem(KeyAttribute... primaryKeyComponents) { - return deleteItemDelegate.deleteItem(primaryKeyComponents); - } - - @Override - public DeleteItemOutcome deleteItem(PrimaryKey primaryKey) { - return deleteItemDelegate.deleteItem(primaryKey); - } - - @Override - public DeleteItemOutcome deleteItem(PrimaryKey primaryKey, - Expected... expected) { - return deleteItemDelegate.deleteItem(primaryKey, expected); - } - - @Override - public DeleteItemOutcome deleteItem(PrimaryKey primaryKey, - String conditionExpression, Map nameMap, - Map valueMap) { - return deleteItemDelegate.deleteItem(primaryKey, - conditionExpression, nameMap, valueMap); - } - - @Override - public DeleteItemOutcome deleteItem(DeleteItemSpec spec) { - return deleteItemDelegate.deleteItem(spec); - } - - /** - * Updates the provisioned throughput for this table. Setting the - * throughput for a table helps you manage performance and is part of the - * provisioned throughput feature of DynamoDB. - *

    - * The provisioned throughput values can be upgraded or downgraded based - * on the maximums and minimums listed in the - * Limits - * section in the Amazon DynamoDB Developer Guide. - *

    - * This table must be in the ACTIVE state for this operation - * to succeed. UpdateTable is an asynchronous operation; while - * executing the operation, the table is in the UPDATING - * state. While the table is in the UPDATING state, the - * table still has the provisioned throughput from before the call. The - * new provisioned throughput setting is in effect only when the table - * returns to the ACTIVE state after the UpdateTable - * operation. - *

    - * You can create, update or delete indexes using UpdateTable. - *

    - * - * @param spec used to specify all the detailed parameters - * - * @return the updated table description returned from DynamoDB. - */ - public TableDescription updateTable(UpdateTableSpec spec) { - UpdateTableRequest.Builder reqBuilder = spec.getRequest().toBuilder(); - reqBuilder.tableName(getTableName()); - UpdateTableRequest updated = reqBuilder.build(); - UpdateTableResponse result = client.updateTable(updated); - TableDescription description = result.tableDescription(); - this.tableDescription = description; - - return description; - } - - /** - * Creates a global secondary index (GSI) with only a hash key on this - * table. Involves network calls. This table must be in the - * ACTIVE state for this operation to succeed. Creating a - * global secondary index is an asynchronous operation; while executing the - * operation, the index is in the CREATING state. Once created, - * the index will be in ACTIVE state. - * - * @param create - * used to specify the details of the index creation - * @param hashKeyDefinition - * used to specify the attribute for describing the key schema - * for the hash key of the GSI to be created for this table. - * - * @return the index being created - */ - public Index createGsi( - CreateGlobalSecondaryIndexAction create, - AttributeDefinition hashKeyDefinition) { - return doCreateGsi(create, hashKeyDefinition); - } - - /** - * Creates a global secondary index (GSI) with both a hash key and a range - * key on this table. Involves network calls. This table must be in the - * ACTIVE state for this operation to succeed. Creating a - * global secondary index is an asynchronous operation; while executing the - * operation, the index is in the CREATING state. Once created, - * the index will be in ACTIVE state. - * - * @param create - * used to specify the details of the index creation - * @param hashKeyDefinition - * used to specify the attribute for describing the key schema - * for the hash key of the GSI to be created for this table. - * @param rangeKeyDefinition - * used to specify the attribute for describing the key schema - * for the range key of the GSI to be created for this table. - * - * @return the index being created - */ - public Index createGsi( - CreateGlobalSecondaryIndexAction create, - AttributeDefinition hashKeyDefinition, - AttributeDefinition rangeKeyDefinition) { - return doCreateGsi(create, hashKeyDefinition, rangeKeyDefinition); - } - - private Index doCreateGsi( - CreateGlobalSecondaryIndexAction create, - AttributeDefinition... keyDefinitions) { - UpdateTableSpec spec = new UpdateTableSpec() - .withAttributeDefinitions(keyDefinitions) - .withGlobalSecondaryIndexUpdates(GlobalSecondaryIndexUpdate.builder() - .create(create).build()); - updateTable(spec); - return this.getIndex(create.indexName()); - } - - /** - * Updates the provisioned throughput for this table. Setting the - * throughput for a table helps you manage performance and is part of the - * provisioned throughput feature of DynamoDB. - *

    - * The provisioned throughput values can be upgraded or downgraded based - * on the maximums and minimums listed in the - * Limits - * section in the Amazon DynamoDB Developer Guide. - *

    - * This table must be in the ACTIVE state for this operation - * to succeed. UpdateTable is an asynchronous operation; while - * executing the operation, the table is in the UPDATING - * state. While the table is in the UPDATING state, the - * table still has the provisioned throughput from before the call. The - * new provisioned throughput setting is in effect only when the table - * returns to the ACTIVE state after the UpdateTable - * operation. - *

    - * You can create, update or delete indexes using UpdateTable. - *

    - * - * @param provisionedThroughput target provisioned throughput - * - * @return the updated table description returned from DynamoDB. - */ - public TableDescription updateTable( - ProvisionedThroughput provisionedThroughput) { - return updateTable(new UpdateTableSpec() - .withProvisionedThroughput(provisionedThroughput)); - } - - /** - * A convenient blocking call that can be used, typically during table - * creation, to wait for the table to become active. This method uses - * {@link software.amazon.awssdk.services.dynamodb.waiters.AmazonDynamoDBWaiters} - * to poll the status of the table every 5 seconds. - * - * @return the table description when the table has become active - * - * @throws IllegalArgumentException if the table is being deleted - * @throws ResourceNotFoundException if the table doesn't exist - */ - public TableDescription waitForActive() throws InterruptedException { - throw new UnsupportedOperationException(); - } - - /** - * A convenient blocking call that can be used, typically during table - * deletion, to wait for the table to become deleted. This method uses - * {@link software.amazon.awssdk.services.dynamodb.waiters.AmazonDynamoDBWaiters} - * to poll the status of the table every 5 seconds. - */ - public void waitForDelete() throws InterruptedException { - throw new UnsupportedOperationException(); - } - - /** - * A convenient blocking call that can be used to wait on a table until it - * has either become active or deleted (ie no longer exists) by polling the - * table every 5 seconds. - * - * @return the table description if the table has become active; or null - * if the table has been deleted. - * - * @deprecated If this method is called immediately after - * {@link DynamoDbClient#createTable(CreateTableRequest)} or - * {@link DynamoDbClient#deleteTable(DeleteTableRequest)} operation, - * the result might be incorrect as all {@link DynamoDbClient} - * operations are eventually consistent and might have a few seconds delay before the status is changed. - */ - @Deprecated - public TableDescription waitForActiveOrDelete() throws InterruptedException { - try { - for (; ; ) { - TableDescription desc = describe(); - if (desc.tableStatus() == TableStatus.ACTIVE) { - return desc; - } else { - Thread.sleep(SLEEP_TIME_MILLIS); - } - } - } catch (ResourceNotFoundException deleted) { - // Ignored or expected. - } - return null; - } - - /** - * A convenient blocking call that can be used to wait on a table and all - * it's indexes until both the table and it's indexes have either become - * active or deleted (ie no longer exists) by polling the table every 5 - * seconds. - * - * @return the table description if the table and all it's indexes have - * become active; or null if the table has been deleted. - * - * @deprecated If this method is called immediately after - * {@link DynamoDbClient#createTable(CreateTableRequest)} or - * {@link DynamoDbClient#deleteTable(DeleteTableRequest)} operation, - * the result might be incorrect as all {@link DynamoDbClient} - * operations are eventually consistent and might have a few seconds delay before the status is changed. - */ - @Deprecated - public TableDescription waitForAllActiveOrDelete() throws InterruptedException { - try { - retry: - for (; ; ) { - TableDescription desc = describe(); - if (desc.tableStatus() == TableStatus.ACTIVE) { - List descriptions = - desc.globalSecondaryIndexes(); - if (descriptions != null) { - for (GlobalSecondaryIndexDescription d : descriptions) { - if (d.indexStatus() != IndexStatus.ACTIVE) { - // Some index is not active. Keep waiting. - Thread.sleep(SLEEP_TIME_MILLIS); - continue retry; - } - } - } - return desc; - } - Thread.sleep(SLEEP_TIME_MILLIS); - continue; - } - } catch (ResourceNotFoundException deleted) { - // Ignored or expected. - } - return null; - } - - /** - * Deletes the table from DynamoDB. Involves network calls. - */ - public DeleteTableResponse delete() { - return client.deleteTable(DeleteTableRequest.builder().tableName(tableName).build()); - } - - @Override - public Item getItem(KeyAttribute... primaryKeyComponents) { - return getItemDelegate.getItem(primaryKeyComponents); - } - - @Override - public Item getItem(PrimaryKey primaryKey) { - return getItemDelegate.getItem(primaryKey); - } - - @Override - public Item getItem(PrimaryKey primaryKey, String projectionExpression, - Map nameMap) { - return getItemDelegate.getItem(primaryKey, projectionExpression, nameMap); - } - - @Override - public Item getItem(GetItemSpec spec) { - return getItemDelegate.getItem(spec); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue) { - return getItemDelegate.getItemOutcome(hashKeyName, hashKeyValue); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return getItemDelegate.getItemOutcome(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue) { - return getItemDelegate.getItem(hashKeyName, hashKeyValue); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return getItemDelegate.getItem(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition) { - return queryDelegate.query(hashKeyName, hashKeyValue, rangeKeyCondition); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - QueryFilter... queryFilters) { - return queryDelegate.query(hashKeyName, hashKeyValue, - rangeKeyCondition, queryFilters); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - String filterExpression, Map nameMap, - Map valueMap) { - return queryDelegate.query(hashKeyName, hashKeyValue, - rangeKeyCondition, filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - String filterExpression, String projectionExpression, - Map nameMap, Map valueMap) { - return queryDelegate.query(hashKeyName, hashKeyValue, - rangeKeyCondition, filterExpression, projectionExpression, - nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, AttributeUpdate... attributeUpdates) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - AttributeUpdate... attributeUpdates) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, Collection expected, - AttributeUpdate... attributeUpdates) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - expected, attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - Collection expected, AttributeUpdate... attributeUpdates) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, - expected, attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, String updateExpression, - Map nameMap, Map valueMap) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - updateExpression, nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String updateExpression, - Map nameMap, - Map valueMap) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, - updateExpression, nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - updateExpression, conditionExpression, - nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap) { - return updateItemDelegate.updateItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, - updateExpression, conditionExpression, - nameMap, valueMap); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, - Object hashKeyValue, String projectionExpression, - Map nameMap) { - return getItemDelegate.getItemOutcome(hashKeyName, hashKeyValue, - projectionExpression, nameMap); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - String projectionExpression, Map nameMap) { - return getItemDelegate.getItemOutcome(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, projectionExpression, nameMap); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue, - String projectionExpression, Map nameMap) { - return getItemDelegate.getItem(hashKeyName, hashKeyValue, - projectionExpression, nameMap); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String projectionExpression, Map nameMap) { - return getItemDelegate.getItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, projectionExpression, nameMap); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue) { - return deleteItemDelegate.deleteItem(hashKeyName, hashKeyValue); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue) { - return deleteItemDelegate.deleteItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, Expected... expected) { - return deleteItemDelegate.deleteItem(hashKeyName, hashKeyValue, - expected); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - Expected... expected) { - return deleteItemDelegate.deleteItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, expected); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String conditionExpression, - Map nameMap, Map valueMap) { - return deleteItemDelegate.deleteItem(hashKeyName, hashKeyValue, - conditionExpression, nameMap, valueMap); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - String conditionExpression, Map nameMap, - Map valueMap) { - return deleteItemDelegate.deleteItem(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue, - conditionExpression, nameMap, valueMap); - } - - @Override - public String toString() { - return "{" + tableName + ": " + tableDescription + "}"; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableCollection.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableCollection.java deleted file mode 100644 index 7e7fe5ddd8c3..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableCollection.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import software.amazon.awssdk.services.dynamodb.document.internal.PageBasedCollection; -import software.amazon.awssdk.services.dynamodb.document.internal.PageIterable; - -/** - * A collection of Table's (initialized with the respective table - * names). - * - * An TableCollection object maintains a cursor pointing to its - * current pages of data. Initially the cursor is positioned before the first - * page. The next method moves the cursor to the next row, and because it - * returns false when there are no more rows in the TableCollection - * object, it can be used in a while loop to iterate through the collection. - * - * Network calls can be triggered when the collection is iterated across page - * boundaries. - * - * @param low level result type - */ -public abstract class TableCollection extends PageBasedCollection { - - // Overriding these just so javadocs will show up. - - /** - * Returns an {@code Iterable>} that iterates over pages of - * tables from this collection. Each call to {@code Iterator.next} on an - * {@code Iterator} returned from this {@code Iterable} results in exactly - * one call to DynamoDB to retrieve a single page of results. - *

    - * - * TableCollection<?> collection = ...; - * for (Page<Table> page : collection.pages()) { - * processTables(page); - * } - * - *

    - * The use of the internal/undocumented {@code PageIterable} class instead - * of {@code Iterable} in the public interface here is retained for - * backwards compatibility. It doesn't expose any methods beyond those - * of the {@code Iterable} interface. This method will be changed to return - * an {@code Iterable>} directly in a future release of the - * SDK. - * - * @see Page - */ - @Override - public PageIterable pages() { - return super.pages(); - } - - /** - * Returns the maximum number of resources to be retrieved in this - * collection; or null if there is no limit. - */ - @Override - public abstract Integer getMaxResultSize(); - - /** - * Returns the low-level result last retrieved (for the current page) from - * the server side; or null if there has yet no calls to the server. - */ - @Override - public R getLastLowLevelResult() { - return super.getLastLowLevelResult(); - } - - /** - * Used to register a listener for the event of receiving a low-level result - * from the server side. - * - * @param listener - * listener to be registered. If null, a "none" listener will be - * set. - * @return the previously registered listener. The return value is never - * null. - */ - @Override - public LowLevelResultListener registerLowLevelResultListener( - LowLevelResultListener listener) { - - return super.registerLowLevelResultListener(listener); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableKeysAndAttributes.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableKeysAndAttributes.java deleted file mode 100644 index d5fadcb866a0..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableKeysAndAttributes.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Used to specify one or multiple primary keys of a specific table, the - * attributes to be retrieved from that table, and the consistency of the read - * operation in a BatchGetItem request. - */ -public class TableKeysAndAttributes { - private final String tableName; - private List primaryKeys; - private Set attributeNames; - private boolean consistentRead; - - private String projectionExpression; - private Map nameMap; - - public TableKeysAndAttributes(String tableName) { - if (tableName == null || tableName.trim().length() == 0) { - throw new IllegalArgumentException("table name must not be null or empty"); - } - this.tableName = tableName; - } - - /** - * Return the list of primary keys (of the current table) to be included in - * a batch get-item operation. - */ - public List getPrimaryKeys() { - return primaryKeys; - } - - /** - * Used to specify multiple primary keys. A primary key could consist of - * either a hash-key or both a hash-key and a range-key depending on the - * schema of the table. - */ - public TableKeysAndAttributes withPrimaryKeys(PrimaryKey... primaryKeys) { - if (primaryKeys == null) { - this.primaryKeys = null; - } else { - Set pkNameSet = null; - for (PrimaryKey pk : primaryKeys) { - if (pkNameSet == null) { - pkNameSet = pk.getComponentNameSet(); - } else { - if (!pkNameSet.equals(pk.getComponentNameSet())) { - throw new IllegalArgumentException( - "primary key attribute names must be consistent for the specified primary keys"); - } - } - } - this.primaryKeys = new ArrayList(Arrays.asList(primaryKeys)); - } - return this; - } - - /** - * Used to specify multiple hash-only primary keys. - * @param hashKeyName hash-only key name - * @param hashKeyValues a list of hash key values - */ - public TableKeysAndAttributes withHashOnlyKeys(String hashKeyName, Object... hashKeyValues) { - if (hashKeyName == null) { - throw new IllegalArgumentException(); - } - PrimaryKey[] primaryKeys = new PrimaryKey[hashKeyValues.length]; - for (int i = 0; i < hashKeyValues.length; i++) { - primaryKeys[i] = new PrimaryKey(hashKeyName, hashKeyValues[i]); - } - return withPrimaryKeys(primaryKeys); - } - - /** - * Used to specify multiple hash-and-range primary keys. - * - * @param hashKeyName - * hash key name - * @param rangeKeyName - * range key name - * @param alternatingHashAndRangeKeyValues - * a list of alternating hash key value and range key value - */ - public TableKeysAndAttributes withHashAndRangeKeys( - String hashKeyName, String rangeKeyName, - Object... alternatingHashAndRangeKeyValues) { - if (hashKeyName == null) { - throw new IllegalArgumentException("hash key name must be specified"); - } - if (rangeKeyName == null) { - throw new IllegalArgumentException("range key name must be specified"); - } - if (alternatingHashAndRangeKeyValues.length % 2 != 0) { - throw new IllegalArgumentException("number of hash and range key values must be the same"); - } - final int len = alternatingHashAndRangeKeyValues.length / 2; - PrimaryKey[] primaryKeys = new PrimaryKey[len]; - for (int i = 0; i < alternatingHashAndRangeKeyValues.length; i += 2) { - primaryKeys[i >> 1] = new PrimaryKey( - hashKeyName, alternatingHashAndRangeKeyValues[i], - rangeKeyName, alternatingHashAndRangeKeyValues[i + 1]); - } - return withPrimaryKeys(primaryKeys); - } - - /** - * Adds a primary key to be included in the batch get-item operation. A - * primary key could consist of either a hash-key or both a - * hash-key and a range-key depending on the schema of the table. - */ - public TableKeysAndAttributes addPrimaryKey(PrimaryKey primaryKey) { - if (primaryKey != null) { - if (primaryKeys == null) { - primaryKeys = new ArrayList(); - } - checkConsistency(primaryKey); - this.primaryKeys.add(primaryKey); - } - return this; - } - - private void checkConsistency(PrimaryKey primaryKey) { - if (this.primaryKeys.size() > 0) { - // use the first one as the representative - final Set nameSet = primaryKeys.get(0).getComponentNameSet(); - if (!nameSet.equals(primaryKey.getComponentNameSet())) { - throw new IllegalArgumentException( - "primary key must be added with consistent key attribute name(s)"); - } - } - } - - /** - * Adds a hash-only primary key to be included in the batch get-item - * operation. - * - * @param hashKeyName name of the hash key attribute name - * @param hashKeyValue name of the hash key value - * @return the current instance for method chaining purposes - */ - public TableKeysAndAttributes addHashOnlyPrimaryKey( - String hashKeyName, Object hashKeyValue) { - this.addPrimaryKey(new PrimaryKey(hashKeyName, hashKeyValue)); - return this; - } - - /** - * Adds multiple hash-only primary keys to be included in the batch get-item - * operation. - * - * @param hashKeyName name of the hash key attribute name - * @param hashKeyValues multiple hash key values - * @return the current instance for method chaining purposes - */ - public TableKeysAndAttributes addHashOnlyPrimaryKeys(String hashKeyName, - Object... hashKeyValues) { - for (Object hashKeyValue : hashKeyValues) { - this.addPrimaryKey(new PrimaryKey(hashKeyName, hashKeyValue)); - } - return this; - } - - /** - * Adds multiple hash-and-range primary keys to be included in the batch - * get-item operation. - * - * @param hashKeyName - * name of the hash key attribute name - * @param rangeKeyName - * name of the range key attribute name - * @param alternatingHashRangeKeyValues - * used to specify multiple alternating hash key and range key - * values - * @return the current instance for method chaining purposes - */ - public TableKeysAndAttributes addHashAndRangePrimaryKeys( - String hashKeyName, String rangeKeyName, - Object... alternatingHashRangeKeyValues) { - if (alternatingHashRangeKeyValues.length % 2 != 0) { - throw new IllegalArgumentException( - "The multiple hash and range key values must alternate"); - } - for (int i = 0; i < alternatingHashRangeKeyValues.length; i += 2) { - Object hashKeyValue = alternatingHashRangeKeyValues[i]; - Object rangeKeyValue = alternatingHashRangeKeyValues[i + 1]; - this.addPrimaryKey( - new PrimaryKey() - .addComponent(hashKeyName, hashKeyValue) - .addComponent(rangeKeyName, rangeKeyValue)); - } - return this; - } - - /** - * Adds a primary key (that consists of a hash-key and a range-key) to be - * included in the batch get-item operation. - * - * @param hashKeyName hash key attribute name - * @param hashKeyValue hash key value - * @param rangeKeyName range key attribute name - * @param rangeKeyValue range key value - * @return the current instance for method chaining purposes - */ - public TableKeysAndAttributes addHashAndRangePrimaryKey( - String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - this.addPrimaryKey( - new PrimaryKey() - .addComponent(hashKeyName, hashKeyValue) - .addComponent(rangeKeyName, rangeKeyValue)); - return this; - } - - /** - * Used to specify the attributes to be retrieved in each item returned - * from the batch get-item operation. - * - * @param attributeNames names of the attributes to be retrieved in each - * item returned from the batch get-item operation. - * @return the current instance for method chaining purposes - */ - public TableKeysAndAttributes withAttributeNames(String... attributeNames) { - if (attributeNames == null) { - this.attributeNames = null; - } else { - this.attributeNames = Collections.unmodifiableSet( - new LinkedHashSet(Arrays.asList(attributeNames))); - } - return this; - } - - public TableKeysAndAttributes withAttributeNames(List attributeNames) { - if (attributeNames == null) { - this.attributeNames = null; - } else { - this.attributeNames = Collections.unmodifiableSet( - new LinkedHashSet(attributeNames)); - } - return this; - } - - public Set getAttributeNames() { - return attributeNames; - } - - public String getTableName() { - return tableName; - } - - public boolean isConsistentRead() { - return consistentRead; - } - - public TableKeysAndAttributes withConsistentRead(boolean consistentRead) { - this.consistentRead = consistentRead; - return this; - } - - public String getProjectionExpression() { - return projectionExpression; - } - - public TableKeysAndAttributes withProjectionExpression(String projectionExpression) { - this.projectionExpression = projectionExpression; - return this; - } - - public Map nameMap() { - return nameMap; - } - - public TableKeysAndAttributes withNameMap(Map nameMap) { - this.nameMap = nameMap == null - ? null : Collections.unmodifiableMap(new LinkedHashMap(nameMap)); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableKeysAndAttributesTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableKeysAndAttributesTest.java deleted file mode 100644 index 7c98d7f2985e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableKeysAndAttributesTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.List; -import org.junit.Assert; -import org.junit.Test; - -public class TableKeysAndAttributesTest { - - @Test - public void testNameSetConsistency() { - TableKeysAndAttributes t = new TableKeysAndAttributes("myTable") - .withHashAndRangeKeys( - // specify the hash key name and range key name once - "foo", "bar", - // followed by multiple values - 123, 1, - 123, 2, - 456, 1, - 456, 2, - 456, 3); - List keys = t.getPrimaryKeys(); - Assert.assertTrue(5 == keys.size()); - for (PrimaryKey key : keys) { - Assert.assertTrue(key.getComponentNameSet().contains("foo")); - Assert.assertTrue(key.getComponentNameSet().contains("bar")); - System.out.println(key); - } - System.out.println(keys); - } - - @Test(expected = IllegalArgumentException.class) - public void testNameSetInConsistency() { - new TableKeysAndAttributes("myTable") - .withPrimaryKeys( - new PrimaryKey("foo", 123, "bar", 345), - new PrimaryKey("foo", 123, "ba", 345)); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableWriteItems.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableWriteItems.java deleted file mode 100644 index d0bcc2e6bf28..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/TableWriteItems.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * Used to specify items to be put and/or primary keys to be deleted from a - * specific table in a BatchWriteItem request. - */ -public class TableWriteItems { - private final String tableName; - private List primaryKeysToDelete; - private Collection itemsToPut; - - public TableWriteItems(String tableName) { - if (tableName == null || tableName.trim().length() == 0) { - throw new IllegalArgumentException("table name must not be null or empty"); - } - this.tableName = tableName; - } - - /** - * Return the list of primary keys (of the current table) to be deleted in - * a batch write operation. - */ - public List getPrimaryKeysToDelete() { - return primaryKeysToDelete; - } - - /** - * Used to specify multiple primary keys to be deleted from the current - * table. A primary key could consist of either a hash-key or both a - * hash-key and a range-key depending on the schema of the table. - */ - public TableWriteItems withPrimaryKeysToDelete( - PrimaryKey... primaryKeysToDelete) { - if (primaryKeysToDelete == null) { - this.primaryKeysToDelete = null; - } else { - Set pkNameSet = null; - for (PrimaryKey pk : primaryKeysToDelete) { - if (pkNameSet == null) { - pkNameSet = pk.getComponentNameSet(); - } else { - if (!pkNameSet.equals(pk.getComponentNameSet())) { - throw new IllegalArgumentException( - "primary key attribute names must be consistent for the specified primary keys"); - } - } - } - this.primaryKeysToDelete = new ArrayList( - Arrays.asList(primaryKeysToDelete)); - } - return this; - } - - /** - * Used to specify multiple hash-only primary keys to be deleted from the - * current table. - * - * @param hashKeyName - * hash-only key name - * @param hashKeyValues - * a list of hash key values - */ - public TableWriteItems withHashOnlyKeysToDelete(String hashKeyName, - Object... hashKeyValues) { - if (hashKeyName == null) { - throw new IllegalArgumentException(); - } - PrimaryKey[] primaryKeys = new PrimaryKey[hashKeyValues.length]; - for (int i = 0; i < hashKeyValues.length; i++) { - primaryKeys[i] = new PrimaryKey(hashKeyName, hashKeyValues[i]); - } - return withPrimaryKeysToDelete(primaryKeys); - } - - /** - * Used to specify multiple hash-and-range primary keys to be deleted - * from the current table. - * - * @param hashKeyName - * hash key name - * @param rangeKeyName - * range key name - * @param alternatingHashAndRangeKeyValues - * a list of alternating hash key value and range key value - */ - public TableWriteItems withHashAndRangeKeysToDelete( - String hashKeyName, String rangeKeyName, - Object... alternatingHashAndRangeKeyValues) { - if (hashKeyName == null) { - throw new IllegalArgumentException("hash key name must be specified"); - } - if (rangeKeyName == null) { - throw new IllegalArgumentException("range key name must be specified"); - } - if (alternatingHashAndRangeKeyValues.length % 2 != 0) { - throw new IllegalArgumentException("number of hash and range key values must be the same"); - } - final int len = alternatingHashAndRangeKeyValues.length / 2; - PrimaryKey[] primaryKeys = new PrimaryKey[len]; - for (int i = 0; i < alternatingHashAndRangeKeyValues.length; i += 2) { - primaryKeys[i >> 1] = new PrimaryKey( - hashKeyName, alternatingHashAndRangeKeyValues[i], - rangeKeyName, alternatingHashAndRangeKeyValues[i + 1]); - } - return withPrimaryKeysToDelete(primaryKeys); - } - - /** - * Adds a primary key to be deleted in a batch write-item operation. A - * primary key could consist of either a hash-key or both a - * hash-key and a range-key depending on the schema of the table. - */ - public TableWriteItems addPrimaryKeyToDelete(PrimaryKey primaryKey) { - if (primaryKey != null) { - if (primaryKeysToDelete == null) { - primaryKeysToDelete = new ArrayList(); - } - checkConsistency(primaryKey); - this.primaryKeysToDelete.add(primaryKey); - } - return this; - } - - private void checkConsistency(PrimaryKey primaryKey) { - if (this.primaryKeysToDelete.size() > 0) { - // use the first one as the representative - final Set nameSet = primaryKeysToDelete.get(0).getComponentNameSet(); - if (!nameSet.equals(primaryKey.getComponentNameSet())) { - throw new IllegalArgumentException( - "primary key must be added with consistent key attribute name(s)"); - } - } - } - - /** - * Adds a hash-only primary key to be deleted in a batch write - * operation. - * - * @param hashKeyName name of the hash key attribute name - * @param hashKeyValue name of the hash key value - * @return the current instance for method chaining purposes - */ - public TableWriteItems addHashOnlyPrimaryKeyToDelete( - String hashKeyName, Object hashKeyValue) { - this.addPrimaryKeyToDelete(new PrimaryKey(hashKeyName, hashKeyValue)); - return this; - } - - /** - * Adds multiple hash-only primary keys to be deleted in a batch write - * operation. - * - * @param hashKeyName name of the hash key attribute name - * @param hashKeyValues multiple hash key values - * @return the current instance for method chaining purposes - */ - public TableWriteItems addHashOnlyPrimaryKeysToDelete(String hashKeyName, - Object... hashKeyValues) { - for (Object hashKeyValue : hashKeyValues) { - this.addPrimaryKeyToDelete(new PrimaryKey(hashKeyName, hashKeyValue)); - } - return this; - } - - /** - * Adds multiple hash-and-range primary keys to be deleted in a batch - * write operation. - * - * @param hashKeyName - * name of the hash key attribute name - * @param rangeKeyName - * name of the range key attribute name - * @param alternatingHashRangeKeyValues - * used to specify multiple alternating hash key and range key - * values - * @return the current instance for method chaining purposes - */ - public TableWriteItems addHashAndRangePrimaryKeysToDelete( - String hashKeyName, String rangeKeyName, - Object... alternatingHashRangeKeyValues) { - if (alternatingHashRangeKeyValues.length % 2 != 0) { - throw new IllegalArgumentException( - "The multiple hash and range key values must alternate"); - } - for (int i = 0; i < alternatingHashRangeKeyValues.length; i += 2) { - Object hashKeyValue = alternatingHashRangeKeyValues[i]; - Object rangeKeyValue = alternatingHashRangeKeyValues[i + 1]; - this.addPrimaryKeyToDelete( - new PrimaryKey() - .addComponent(hashKeyName, hashKeyValue) - .addComponent(rangeKeyName, rangeKeyValue)); - } - return this; - } - - /** - * Adds a primary key (that consists of a hash-key and a range-key) to be - * deleted in a batch write operation. - * - * @param hashKeyName hash key attribute name - * @param hashKeyValue hash key value - * @param rangeKeyName range key attribute name - * @param rangeKeyValue range key value - * @return the current instance for method chaining purposes - */ - public TableWriteItems addHashAndRangePrimaryKeyToDelete( - String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - this.addPrimaryKeyToDelete( - new PrimaryKey() - .addComponent(hashKeyName, hashKeyValue) - .addComponent(rangeKeyName, rangeKeyValue)); - return this; - } - - /** - * Used to specify the items to be put in the current table in a batch write - * operation. - * - * @return the current instance for method chaining purposes - */ - public TableWriteItems withItemsToPut(Item... itemsToPut) { - if (itemsToPut == null) { - this.itemsToPut = null; - } else { - this.itemsToPut = new ArrayList(Arrays.asList(itemsToPut)); - } - return this; - } - - /** - * Used to specify the collection of items to be put in the current table in - * a batch write operation. - * - * @return the current instance for method chaining purposes - */ - public TableWriteItems withItemsToPut(Collection itemsToPut) { - if (itemsToPut == null) { - this.itemsToPut = null; - } else { - this.itemsToPut = new ArrayList(itemsToPut); - } - return this; - } - - /** - * Returns the collection of items to be put in the current table in - * a batch write operation. - */ - public Collection getItemsToPut() { - return itemsToPut == null - ? null - : Collections.unmodifiableCollection(itemsToPut); - } - - public String getTableName() { - return tableName; - } - - /** - * Adds an item to be put to the current table in a batch write operation. - */ - public TableWriteItems addItemToPut(Item item) { - if (item != null) { - if (itemsToPut == null) { - itemsToPut = new ArrayList(); - } - this.itemsToPut.add(item); - } - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/UpdateItemOutcome.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/UpdateItemOutcome.java deleted file mode 100644 index 6169b8906d44..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/UpdateItemOutcome.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse; - -/** - * The outcome of updating an item in a DynamoDB table. - */ -public class UpdateItemOutcome { - private final UpdateItemResponse result; - - /** - * @param result the low-level result; must not be null - */ - public UpdateItemOutcome(UpdateItemResponse result) { - if (result == null) { - throw new IllegalArgumentException(); - } - this.result = result; - } - - /** - * Returns all the returned attributes as a (non-null) {@link Item}. - */ - public Item getItem() { - Map attributes = - InternalUtils.toSimpleMapValue(result.attributes()); - Item item = Item.fromMap(attributes); - return item; - } - - /** - * Returns a non-null low-level result returned from the server side. - */ - public UpdateItemResponse getUpdateItemResponse() { - return result; - } - - @Override - public String toString() { - return String.valueOf(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/BatchGetItemApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/BatchGetItemApi.java deleted file mode 100644 index 599c4f35c7c5..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/BatchGetItemApi.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.BatchGetItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.TableKeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchGetItemSpec; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; - -/** - * DynamoDB BatchGetItem API that can be used to retrieve multiple items from - * multiple tables in one request/response by specifying one or multiple primary - * keys per table in the request. - */ -@ThreadSafe -public interface BatchGetItemApi { - /** - * Used to perform a batch get-item operation from DynamoDB. - * - * @param returnConsumedCapacity - * returned capacity to be returned - * @param tableKeyAndAttributes - * the tables, keys, and attributes specification to be used to - * retrieve the items. - */ - BatchGetItemOutcome batchGetItem( - ReturnConsumedCapacity returnConsumedCapacity, - TableKeysAndAttributes... tableKeyAndAttributes); - - /** - * Used to perform a batch get-item operation from DynamoDB. - * - * @param tableKeyAndAttributes - * the tables, keys, and attributes specification to be used to - * retrieve the items. - */ - BatchGetItemOutcome batchGetItem( - TableKeysAndAttributes... tableKeyAndAttributes); - - /** - * Used to perform a batch get-item operation from DynamoDB with full - * parameter specification. - */ - BatchGetItemOutcome batchGetItem(BatchGetItemSpec spec); - - /** - * Used to perform a batch get-item for the unprocessed keys returned from a - * previous batch get-item operation. - * - * @param returnConsumedCapacity - * returned capacity to be returned - * @param unprocessedKeys - * the unprocessed keys returned from the result of a previous - * batch-get-item operation. - * - * @see BatchGetItemOutcome#getUnprocessedKeys() - */ - BatchGetItemOutcome batchGetItemUnprocessed( - ReturnConsumedCapacity returnConsumedCapacity, - Map unprocessedKeys); - - /** - * Used to perform a batch get-item for the unprocessed keys returned from a - * previous batch get-item operation. - * - * @param unprocessedKeys - * the unprocessed keys returned from the result of a previous - * batch-get-item operation. - * - * @see BatchGetItemOutcome#getUnprocessedKeys() - */ - BatchGetItemOutcome batchGetItemUnprocessed( - Map unprocessedKeys); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/BatchWriteItemApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/BatchWriteItemApi.java deleted file mode 100644 index de51ec95b11c..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/BatchWriteItemApi.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.BatchWriteItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.TableWriteItems; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchWriteItemSpec; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * DynamoDB BatchWriteItem API that can be used to put multiple items to and/or - * delete multiple items from multiple tables in a single request-response - * to/from DynamoDB. - */ -@ThreadSafe -public interface BatchWriteItemApi { - - /** - * Used to perform a batch write operation to DynamoDB. - * - * @param tableWriteItems - * the tables and the respective keys to delete from and/or the - * respective items to be put. - */ - BatchWriteItemOutcome batchWriteItem( - TableWriteItems... tableWriteItems); - - /** - * Used to perform a batch write operation to DynamoDB with full parameter - * specification. - */ - BatchWriteItemOutcome batchWriteItem(BatchWriteItemSpec spec); - - /** - * Used to perform a batch write operation for the unprocessed items - * returned from a previous batch write operation. - * - * @param unprocessedItems - * the unprocessed items returned from the result of a previous - * batch write operation - * - * @see BatchWriteItemOutcome#getUnprocessedItems() - */ - BatchWriteItemOutcome batchWriteItemUnprocessed( - Map> unprocessedItems); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/DeleteItemApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/DeleteItemApi.java deleted file mode 100644 index 292707b45103..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/DeleteItemApi.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.DeleteItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.spec.DeleteItemSpec; - -/** - * A Table-centric DeleteItem API. - */ -@ThreadSafe -public interface DeleteItemApi { - /** Deletes an item by primary key. */ - DeleteItemOutcome deleteItem(KeyAttribute... primaryKeyComponents); - - /** Deletes an item by primary key. */ - DeleteItemOutcome deleteItem(PrimaryKey primaryKey); - - /** Deletes an item by hash-only primary key. */ - DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue); - - /** Deletes an item by hash key-and-range primary key. */ - DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue); - - /** - * Conditional delete with the specified primary key and expected - * conditions. - */ - DeleteItemOutcome deleteItem(PrimaryKey primaryKey, - Expected... expected); - - /** - * Conditional delete with the specified hash-only primary key and expected - * conditions. - */ - DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue, - Expected... expected); - - /** - * Conditional delete with the specified hash-and-range primary key and - * expected conditions. - */ - DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - Expected... expected); - - /** - * Conditional delete with the specified primary key and condition - * expression. - */ - DeleteItemOutcome deleteItem(PrimaryKey primaryKey, - String conditionExpression, - Map nameMap, - Map valueMap); - - /** - * Conditional delete with the specified hash-only primary key and condition - * expression. - */ - DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue, - String conditionExpression, - Map nameMap, - Map valueMap); - - /** - * Conditional delete with the specified hash-and-range primary key and - * condition expression. - */ - DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String conditionExpression, - Map nameMap, - Map valueMap); - - /** Deletes an item by specifying all the details. */ - DeleteItemOutcome deleteItem(DeleteItemSpec spec); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/GetItemApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/GetItemApi.java deleted file mode 100644 index 262e5fcff6fc..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/GetItemApi.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.GetItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.spec.GetItemSpec; - -/** - * A Table-centric GetItem API. - *

    - * In general, all getter methods in this library incur no network. - * GetItemApi is the only exception due to the fact that the - * web service API is indistinguishable from a Java getter method. - */ -@ThreadSafe -public interface GetItemApi { - - /** - * Retrieves an item and the associated information by primary key. Incurs - * network access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(PrimaryKey primaryKey); - - /** - * Retrieves an item and the associated information by primary key when the - * primary key is a hash-only key. Incurs network access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(KeyAttribute... primaryKeyComponents); - - /** - * Retrieves an item and the associated information by primary key when the - * primary key is a hash-only key. Incurs network access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue); - - /** - * Retrieves an item and the associated information by primary key when the - * primary key consists of both a hash-key and a range-key. Incurs network - * access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue); - - /** - * Retrieves an item and the associated information using projection - * expression. Incurs network access. - * - * @param projectionExpression - * projection expression, example: "a.b , c[0].e" - * - * @param nameMap - * actual values for the attribute-name place holders; can be - * null if there is no attribute-name placeholder. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(PrimaryKey primaryKey, - String projectionExpression, Map nameMap); - - /** - * Retrieves an item and the associated information via the specified hash - * key using projection expression. Incurs network access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String projectionExpression, Map nameMap); - - /** - * Retrieves an item and the associated information via the specified hash - * key and range key using projection expression. Incurs network access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String projectionExpression, Map nameMap); - - /** - * Retrieves an item via the specified hash key using projection expression. - * Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(String hashKeyName, Object hashKeyValue, - String projectionExpression, Map nameMap); - - /** - * Retrieves an item via the specified hash key and range key using - * projection expression. Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String projectionExpression, Map nameMap); - - /** - * Retrieves an item and the associated information by specifying all the - * details. Incurs network access. - * - * @return the (non-null) result of item retrieval. - */ - GetItemOutcome getItemOutcome(GetItemSpec spec); - - /** - * Retrieves an item by primary key; or null if the item doesn't exist. - * Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(PrimaryKey primaryKey); - - /** - * Retrieves an item by primary key. Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(KeyAttribute... primaryKeyComponents); - - /** - * Retrieves an item by primary key when the primary key is a hash-only key. - * Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(String hashKeyName, Object hashKey); - - /** - * Retrieves an item by primary key when the primary key consists of both a - * hash-key and a range-key. Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue); - - /** - * Retrieves an item using projection expression. Incurs network access. - * - * @param projectionExpression - * projection expression, example: "a.b , c[0].e" - * - * @param nameMap - * actual values for the attribute-name place holders; can be - * null if there is no attribute-name placeholder. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(PrimaryKey primaryKey, String projectionExpression, - Map nameMap); - - /** - * Retrieves an item by specifying all the details. Incurs network access. - * - * @return the retrieved item; or null if the item doesn't exist. - */ - Item getItem(GetItemSpec spec); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/ListTablesApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/ListTablesApi.java deleted file mode 100644 index 2c16e9792010..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/ListTablesApi.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.TableCollection; -import software.amazon.awssdk.services.dynamodb.document.spec.ListTablesSpec; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; - -/** - * DynamoDB ListTables API. - */ -@ThreadSafe -public interface ListTablesApi { - /** - * Returns a collection of all the tables (initialized with the respective - * table names) associated with the current account and endpoint. The - * underlying collection is paginated with a page size of 100. A network - * call is made whenever the collection is iterated across a page boundary. - */ - TableCollection listTables(); - - /** - * Returns a collection of tables (initialized with the respective table - * names) associated with the current account and endpoint, starting with a - * name after the specified exclusiveStartTableName . The - * underlying collection is paginated with a page size of 100. A network - * call is made whenever the collection is iterated across a page boundary. - * - * @param exclusiveStartTableName - * The first table name that this operation will evaluate, - * exclusive of the specified - * exclusiveStartTableName. Use the value that was - * returned for LastEvaluatedTableName in a previous - * operation, so that you can obtain the next page of results. - */ - TableCollection listTables(String exclusiveStartTableName); - - /** - * Returns a collection of tables (initialized with the respective table - * names) up to the specified maxResultSize associated with - * the current account and endpoint, starting with a name after the - * specified exclusiveStartTableName. The underlying collection - * is paginated with a page size of 100. A network call is made whenever the - * collection is iterated across a page boundary. - * - * @param exclusiveStartTableName - * The first table name that this operation will evaluate - * exclusive of the specified - * exclusiveStartTableName. Use the value that was - * returned for LastEvaluatedTableName in a previous - * operation, so that you can obtain the next page of results. - * @param maxResultSize - * A maximum number of table names to return. - */ - TableCollection listTables(String exclusiveStartTableName, int maxResultSize); - - /** - * Returns a collection of tables (initialized with the respective table - * names) up to the specified maxResultSize associated with - * the current account and endpoint. The underlying collection - * is paginated with a page size of 100. A network call is made whenever the - * collection is iterated across a page boundary. - * - * @param maxResultSize - * A maximum number of table names to return. - */ - TableCollection listTables(int maxResultSize); - - /** - * List tables by specifying all the details. The underlying collection is - * paginated with the specified page size (which defaults to 100). A network - * call is made whenever the collection is iterated across a page boundary. - * - * - * @param spec - * can be used to specify all the detailed parameters of listing - * tables. - * - * @return a collection of tables associated with the current account and - * endpoint. - */ - TableCollection listTables(ListTablesSpec spec); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/PutItemApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/PutItemApi.java deleted file mode 100644 index b7485c186848..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/PutItemApi.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.PutItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.PutItemSpec; - -/** - * A Table-centric PutItem API. - */ -@ThreadSafe -public interface PutItemApi { - /** - * Unconditional put. - */ - PutItemOutcome putItem(Item item); - - /** - * Conditional put. - */ - PutItemOutcome putItem(Item item, Expected... expected); - - /** - * Conditional put via condition expression. - */ - PutItemOutcome putItem(Item item, String conditionExpression, - Map nameMap, Map valueMap); - - /** Puts an item by specifying all the details. */ - PutItemOutcome putItem(PutItemSpec spec); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/QueryApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/QueryApi.java deleted file mode 100644 index 0d79bb211228..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/QueryApi.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.QueryFilter; -import software.amazon.awssdk.services.dynamodb.document.QueryOutcome; -import software.amazon.awssdk.services.dynamodb.document.RangeKeyCondition; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; - -/** - * A Table-centric Query API. - */ -@ThreadSafe -public interface QueryApi { - /** - * Retrieves items by the specified hash key. - */ - ItemCollection query(String hashKeyName, Object hashKeyValue); - - /** - * Retrieves items by the specified hash key. - */ - ItemCollection query(KeyAttribute hashKey); - - /** - * Retrieves items by the specified hash key and a range key condition. - */ - ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition); - - ItemCollection query(String hashKeyName, Object hashKeyValue, - RangeKeyCondition rangeKeyCondition); - - /** - * Retrieves items by the specified hash key, a range key condition - * and a list of query filters. - */ - ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, QueryFilter... queryFilters); - - ItemCollection query(String hashKeyName, Object hashKeyValue, - RangeKeyCondition rangeKeyCondition, QueryFilter... queryFilters); - - /** - * Retrieves items by the specified hash key, a range key condition, and - * a filter expression string. - * - * @param filterExpression filter expression - * example: "(#a > :a) AND (#c > :c OR #e < :e)" - * - * @param nameMap actual values for the attribute-name place holders; - * can be null if there is no attribute-name placeholder. - * @param valueMap actual values for the value place holders - * can be null if there is no attribute-value placeholder. - */ - ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, - String filterExpression, - Map nameMap, - Map valueMap); - - ItemCollection query(String hashKeyName, Object hashKeyValue, - RangeKeyCondition rangeKeyCondition, - String filterExpression, - Map nameMap, - Map valueMap); - - /** - * Retrieves items by the specified hash key, a range key condition, - * a filter expression and a projection expression. - * - * @param filterExpression filter expression - * example: "(#a > :a) AND (#c > :c OR #e < :e)" - * - * @param projectionExpression projection expression - * example: "a.b, c[0].e" - * - * @param nameMap actual values for the attribute-name place holders; - * can be null if there is no attribute-name placeholder. - * @param valueMap actual values for the value place holders - * can be null if there is no attribute-value placeholder. - */ - ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, - String filterExpression, - String projectionExpression, - Map nameMap, - Map valueMap); - - /** - * Retrieves items by the specified hash key, a range key condition, - * a filter expression and a projection expression. - * - * @param filterExpression filter expression - * example: "(#a > :a) AND (#c > :c OR #e < :e)" - * - * @param projectionExpression projection expression - * example: "a.b, c[0].e" - * - * @param nameMap actual values for the attribute-name place holders; - * can be null if there is no attribute-name placeholder. - * @param valueMap actual values for the value place holders - * can be null if there is no attribute-value placeholder. - */ - ItemCollection query(String hashKeyName, Object hashKeyValue, - RangeKeyCondition rangeKeyCondition, - String filterExpression, - String projectionExpression, - Map nameMap, - Map valueMap); - - /** - * Queries table by specifying all the details. - */ - ItemCollection query(QuerySpec spec); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/ScanApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/ScanApi.java deleted file mode 100644 index f423e927ec9c..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/ScanApi.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.ScanFilter; -import software.amazon.awssdk.services.dynamodb.document.ScanOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; - -/** - * A Table-centric Scan API. - */ -@ThreadSafe -public interface ScanApi { - /** - * Retrieves items by the specified list of scan filters. - */ - ItemCollection scan(ScanFilter... scanFilters); - - /** - * Scans table using a Filter Expression. - * - * @param filterExpression - * condition expression example: - * "(#a > :a) AND (#c > :c OR #e < :e)" - * - * @param nameMap - * actual values for the attribute-name place holders; can be - * null if there is no attribute-name placeholder. - * @param valueMap - * actual values for the value place holders can be null if there - * is no attribute-value placeholder. - */ - ItemCollection scan(String filterExpression, - Map nameMap, - Map valueMap); - - /** - * Scans table using a Filter Expression and a Projection Expression. - * - * @param filterExpression - * condition expression example: - * "(#a > :a) AND (#c > :c OR #e < :e)" - * @param projectionExpression - * projection expression example: "a.b , c[0].e" - * - * @param nameMap actual values for the attribute-name place holders; - * can be null if there is no attribute-name placeholder. - * @param valueMap actual values for the value place holders - * can be null if there is no attribute-value placeholder. - */ - ItemCollection scan( - String filterExpression, - String projectionExpression, - Map nameMap, - Map valueMap); - - /** - * Scans table by specifying all the details. - */ - ItemCollection scan(ScanSpec params); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/UpdateItemApi.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/UpdateItemApi.java deleted file mode 100644 index 6a3bd4661bbb..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/api/UpdateItemApi.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.api; - -import java.util.Collection; -import java.util.Map; -import software.amazon.awssdk.annotations.ThreadSafe; -import software.amazon.awssdk.services.dynamodb.document.AttributeUpdate; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.UpdateItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.UpdateItemSpec; - -/** - * A Table-centric UpdateItem API. - */ -@ThreadSafe -public interface UpdateItemApi { - - /** - * Updates an item with the attributes specified. - * - * @param primaryKey - * primary key of the item to be updated - * @param attributeUpdates - * attributes to be updated - */ - UpdateItemOutcome updateItem(PrimaryKey primaryKey, - AttributeUpdate... attributeUpdates); - - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - AttributeUpdate... attributeUpdates); - - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - AttributeUpdate... attributeUpdates); - - /** - * Updates an item with the attributes specified. - * - * @param primaryKey - * primary key of the item to be updated - * @param expected - * the condition to match for the update to succeed. - * @param attributeUpdates - * attributes to be updated - */ - UpdateItemOutcome updateItem(PrimaryKey primaryKey, - Collection expected, AttributeUpdate... attributeUpdates); - - /** - * Updates an item with the specified hash-only key and attributes. - */ - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - Collection expected, AttributeUpdate... attributeUpdates); - - /** - * Updates an item with the specified hash key, range key and attributes. - */ - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - Collection expected, AttributeUpdate... attributeUpdates); - - /** - * Performs an update on an item in the table using the given update - * expression string. - * - * @param primaryKey - * primary key of the item to be updated - * @param updateExpression - * the update expression that specifies the attributes to be - * updated. - * @param nameMap - * the map containing the mapping between attribute names used in - * update expression and the actual name of the attributes - * @param valueMap - * the map containing the mapping between the attribute value - * used in update expression and the actual value of the - * attribute - */ - UpdateItemOutcome updateItem(PrimaryKey primaryKey, - String updateExpression, Map nameMap, - Map valueMap); - - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String updateExpression, Map nameMap, - Map valueMap); - - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String updateExpression, Map nameMap, - Map valueMap); - - /** - * Updates an item with the specified primary key using the given - * update expression provided the condition expression evaluates to true. - * - * @param primaryKey - * primary key of the item to be updated - * @param updateExpression - * the update expression that specifies the attributes to be - * updated. - * @param conditionExpression - * the condition expression that specifies the condition that - * needs to be evaluated to true - * @param nameMap - * the map containing the mapping between attribute names used in - * update and condition expression and the actual name of the - * attributes - * @param valueMap - * the map containing the mapping between the attribute value - * used in update and condition expression and the actual value - * of the attribute - */ - UpdateItemOutcome updateItem(PrimaryKey primaryKey, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap); - - /** - * Updates an item with the specified hash key using the given - * update expression provided the condition expression evaluates to true. - */ - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap); - - /** - * Updates an item with the specified hash key and range key using the given - * update expression provided the condition expression evaluates to true. - */ - UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap); - - /** - * Performs an update on an item in the table by specifying all the details. - * - * @param updateItemSpec - * the update specification for the item to be updated. - */ - UpdateItemOutcome updateItem(UpdateItemSpec updateItemSpec); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/AbstractImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/AbstractImpl.java deleted file mode 100644 index d20545047122..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/AbstractImpl.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Table; - -/** - * Internal common base class for API implementations. - */ -abstract class AbstractImpl { - private final Table table; - private final DynamoDbClient client; - - protected AbstractImpl(DynamoDbClient client, Table table) { - this.client = client; - this.table = table; - } - - /** - * Returns the owning table. - */ - public final Table getTable() { - return table; - } - - final DynamoDbClient getClient() { - return client; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/BatchGetItemImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/BatchGetItemImpl.java deleted file mode 100644 index cd6d81bf41cc..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/BatchGetItemImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.BatchGetItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.TableKeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.document.api.BatchGetItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchGetItemSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; - -/** - * The implementation for BatchGetItemApi. - */ -public class BatchGetItemImpl implements BatchGetItemApi { - private final DynamoDbClient client; - - public BatchGetItemImpl(DynamoDbClient client) { - this.client = client; - } - - @Override - public BatchGetItemOutcome batchGetItem( - ReturnConsumedCapacity returnConsumedCapacity, - TableKeysAndAttributes... tableKeysAndAttributes) { - return doBatchGetItem(new BatchGetItemSpec() - .withReturnConsumedCapacity(returnConsumedCapacity) - .withTableKeyAndAttributes(tableKeysAndAttributes)); - } - - @Override - public BatchGetItemOutcome batchGetItem( - TableKeysAndAttributes... tableKeysAndAttributes) { - return doBatchGetItem(new BatchGetItemSpec() - .withTableKeyAndAttributes(tableKeysAndAttributes)); - } - - @Override - public BatchGetItemOutcome batchGetItem(BatchGetItemSpec spec) { - return doBatchGetItem(spec); - } - - private BatchGetItemOutcome doBatchGetItem(BatchGetItemSpec spec) { - final Collection tableKeysAndAttributesCol = - spec.getTableKeysAndAttributes(); - // Unprocessed keys take precedence - Map requestItems = spec.getUnprocessedKeys(); - if (requestItems == null || requestItems.size() == 0) { - // handle new requests only if there is no unprocessed keys - requestItems = new LinkedHashMap(); - } - if (tableKeysAndAttributesCol != null) { - for (TableKeysAndAttributes tableKeysAndAttributes : tableKeysAndAttributesCol) { - // attributes against one table - final Set attrNames = tableKeysAndAttributes.getAttributeNames(); - // primary keys against one table - final List pks = tableKeysAndAttributes.getPrimaryKeys(); - final List> keys = new ArrayList>(pks.size()); - for (PrimaryKey pk : pks) { - keys.add(InternalUtils.toAttributeValueMap(pk)); - } - final KeysAndAttributes keysAndAttrs = KeysAndAttributes.builder() - .attributesToGet(attrNames) - .consistentRead(tableKeysAndAttributes.isConsistentRead()) - .keys(keys) - .projectionExpression(tableKeysAndAttributes.getProjectionExpression()) - .expressionAttributeNames(tableKeysAndAttributes.nameMap()) - .build(); - requestItems.put(tableKeysAndAttributes.getTableName(), keysAndAttrs); - } - } - BatchGetItemRequest req = spec.getRequest() - .toBuilder() - .requestItems(requestItems) - .build(); - spec.setRequest(req); - BatchGetItemResponse result = client.batchGetItem(req); - return new BatchGetItemOutcome(result); - } - - @Override - public BatchGetItemOutcome batchGetItemUnprocessed( - ReturnConsumedCapacity returnConsumedCapacity, - Map unprocessedKeys) { - return doBatchGetItem(new BatchGetItemSpec() - .withReturnConsumedCapacity(returnConsumedCapacity) - .withUnprocessedKeys(unprocessedKeys)); - } - - @Override - public BatchGetItemOutcome batchGetItemUnprocessed( - Map unprocessedKeys) { - return doBatchGetItem(new BatchGetItemSpec() - .withUnprocessedKeys(unprocessedKeys)); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/BatchWriteItemImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/BatchWriteItemImpl.java deleted file mode 100644 index 787371d0452e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/BatchWriteItemImpl.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toAttributeValueMap; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toAttributeValues; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.BatchWriteItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.TableWriteItems; -import software.amazon.awssdk.services.dynamodb.document.api.BatchWriteItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.BatchWriteItemSpec; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemResponse; -import software.amazon.awssdk.services.dynamodb.model.DeleteRequest; -import software.amazon.awssdk.services.dynamodb.model.PutRequest; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * The implementation for BatchWriteItemApi. - */ -public class BatchWriteItemImpl implements BatchWriteItemApi { - private final DynamoDbClient client; - - public BatchWriteItemImpl(DynamoDbClient client) { - this.client = client; - } - - @Override - public BatchWriteItemOutcome batchWriteItem( - TableWriteItems... tableWriteItems) { - return doBatchWriteItem(new BatchWriteItemSpec() - .withTableWriteItems(tableWriteItems)); - } - - @Override - public BatchWriteItemOutcome batchWriteItem(BatchWriteItemSpec spec) { - return doBatchWriteItem(spec); - } - - @Override - public BatchWriteItemOutcome batchWriteItemUnprocessed( - Map> unprocessedItems) { - return doBatchWriteItem(new BatchWriteItemSpec() - .withUnprocessedItems(unprocessedItems)); - } - - private BatchWriteItemOutcome doBatchWriteItem(BatchWriteItemSpec spec) { - final Collection tableWriteItemsCol = - spec.getTableWriteItems(); - // Unprocessed items take precedence - Map> requestItems = - spec.getUnprocessedItems(); - if (requestItems == null || requestItems.size() == 0) { - // handle new requests only if there is no unprocessed items - requestItems = new LinkedHashMap>(); - } - if (tableWriteItemsCol != null) { - for (TableWriteItems tableWriteItems : tableWriteItemsCol) { - // items to be put to a single table - Collection itemsToPut = tableWriteItems.getItemsToPut(); - // primary keys to deleted in a single table - final List pksToDelete = - tableWriteItems.getPrimaryKeysToDelete(); - // Merge them into a list of write requests to a single table - final int numPut = itemsToPut == null ? 0 : itemsToPut.size(); - final int numDel = pksToDelete == null ? 0 : pksToDelete.size(); - final List writeRequests = - new ArrayList(numPut + numDel); - // Put requests for a single table - if (itemsToPut != null) { - for (Item item : itemsToPut) { - writeRequests.add(WriteRequest.builder() - .putRequest(PutRequest.builder() - .item(toAttributeValues(item)) - .build()) - .build()); - } - } - // Delete requests for a single table - if (pksToDelete != null) { - for (PrimaryKey pkToDelete : pksToDelete) { - writeRequests.add(WriteRequest.builder() - .deleteRequest(DeleteRequest.builder() - .key(toAttributeValueMap(pkToDelete)) - .build()) - .build()); - } - } - requestItems.put(tableWriteItems.getTableName(), writeRequests); - } - } - BatchWriteItemRequest req = spec.getRequest().toBuilder() - .requestItems(requestItems).build(); - BatchWriteItemResponse result = client.batchWriteItem(req); - return new BatchWriteItemOutcome(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/DeleteItemImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/DeleteItemImpl.java deleted file mode 100644 index 7e70b43e436e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/DeleteItemImpl.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Collection; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.DeleteItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.api.DeleteItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.DeleteItemSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemResponse; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -/** - * The implementation for DeleteItemApi. - */ -public class DeleteItemImpl extends AbstractImpl implements DeleteItemApi { - public DeleteItemImpl(DynamoDbClient client, Table table) { - super(client, table); - } - - @Override - public DeleteItemOutcome deleteItem(KeyAttribute... primaryKeyComponents) { - return doDeleteItem(new DeleteItemSpec() - .withPrimaryKey(primaryKeyComponents)); - } - - @Override - public DeleteItemOutcome deleteItem(PrimaryKey primaryKey) { - return doDeleteItem(new DeleteItemSpec() - .withPrimaryKey(primaryKey)); - } - - @Override - public DeleteItemOutcome deleteItem(PrimaryKey primaryKeys, - Expected... expected) { - return doDeleteItem(new DeleteItemSpec() - .withPrimaryKey(primaryKeys) - .withExpected(expected)); - } - - @Override - public DeleteItemOutcome deleteItem(PrimaryKey primaryKeys, - String conditionExpression, Map nameMap, - Map valueMap) { - return doDeleteItem(new DeleteItemSpec() - .withPrimaryKey(primaryKeys) - .withConditionExpression(conditionExpression) - .withNameMap(nameMap) - .valueMap(valueMap)) - ; - } - - @Override - public DeleteItemOutcome deleteItem(DeleteItemSpec spec) { - return doDeleteItem(spec); - } - - private DeleteItemOutcome doDeleteItem(DeleteItemSpec spec) { - // set the table name - final String tableName = getTable().getTableName(); - // set up the keys - DeleteItemRequest.Builder requestBuilder = spec.getRequest().toBuilder() - .tableName(tableName) - .key(InternalUtils.toAttributeValueMap(spec.getKeyComponents())); - // set up the expected attribute map, if any - final Collection expected = spec.getExpected(); - final Map expectedMap = - InternalUtils.toExpectedAttributeValueMap(expected); - // set up the value map, if any (when expression API is used) - final Map attrValMap = - InternalUtils.fromSimpleMap(spec.valueMap()); - // set up the request - requestBuilder.expected(expectedMap) - .expressionAttributeNames(spec.nameMap()) - .expressionAttributeValues(attrValMap); - DeleteItemResponse result = getClient().deleteItem(requestBuilder.build()); - return new DeleteItemOutcome(result); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, Object hashKeyValue) { - return deleteItem(new PrimaryKey(hashKeyName, hashKeyValue)); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue) { - return deleteItem( - new PrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue)); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, Expected... expected) { - return deleteItem(new PrimaryKey(hashKeyName, hashKeyValue), expected); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - Expected... expected) { - return deleteItem( - new PrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue), - expected); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String conditionExpression, - Map nameMap, Map valueMap) { - return deleteItem(new PrimaryKey(hashKeyName, hashKeyValue), - conditionExpression, nameMap, valueMap); - } - - @Override - public DeleteItemOutcome deleteItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - String conditionExpression, Map nameMap, - Map valueMap) { - return deleteItem( - new PrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue), - conditionExpression, nameMap, valueMap); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/Filter.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/Filter.java deleted file mode 100644 index c0671c942dcc..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/Filter.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; - -/** - * Abstract base class for both query filters and scan filters. - */ -public abstract class Filter> { - private final String attribute; - private ComparisonOperator op; - private Object[] values; - - /** - * Create a filter for the specified top-level attribute. - * - * @param attrName - * attribute name - */ - protected Filter(String attrName) { - InternalUtils.checkInvalidAttrName(attrName); - this.attribute = attrName; - } - - /** Returns the attribute name. */ - public String getAttribute() { - return attribute; - } - - public ComparisonOperator getComparisonOperator() { - return op; - } - - public Object[] values() { - return values == null ? null : values.clone(); - } - - @SuppressWarnings("unchecked") - protected T values(Object... values) { - this.values = values.clone(); - return (T) this; - } - - @SuppressWarnings("unchecked") - private T withComparisonOperator(ComparisonOperator op) { - this.op = op; - return (T) this; - } - - /** - * Creates and returns a condition of the range key being equal to the given - * value. - */ - public T eq(Object val) { - return withComparisonOperator(ComparisonOperator.EQ).values(val); - } - - public T ne(Object val) { - return withComparisonOperator(ComparisonOperator.NE).values(val); - } - - /** - * Expects the attribute be an existing attribute. - */ - public T exists() { - return withComparisonOperator(ComparisonOperator.NOT_NULL); - } - - /** - * Expects the attribute be non-existing. - */ - public T notExist() { - return withComparisonOperator(ComparisonOperator.NULL); - } - - public T contains(Object val) { - return withComparisonOperator(ComparisonOperator.CONTAINS).values(val); - } - - public T notContains(Object val) { - return withComparisonOperator(ComparisonOperator.NOT_CONTAINS).values(val); - } - - /** - * Creates and returns a condition of the range key with a value that begins - * with the given value. - */ - public T beginsWith(String val) { - return withComparisonOperator(ComparisonOperator.BEGINS_WITH).values(val); - } - - public T in(Object... values) { - if (values == null || values.length == 0) { - throw new IllegalArgumentException("values must not be null or empty."); - } - - return withComparisonOperator(ComparisonOperator.IN).values(values); - } - - /** - * Creates and returns a condition of the range key that has a value between - * the given values. - */ - public T between(Object low, Object hi) { - return withComparisonOperator(ComparisonOperator.BETWEEN).values(low, hi); - } - - /** - * Creates and returns a condition of the range key being greater than or - * equal to the given value. - */ - public T ge(Object val) { - return withComparisonOperator(ComparisonOperator.GE).values(val); - } - - /** - * Creates and returns a condition of the range key being greater than the - * given value. - */ - public T gt(Object val) { - return withComparisonOperator(ComparisonOperator.GT).values(val); - } - - /** - * Creates and returns a condition of the range key being less than or equal - * to the given value. - */ - public T le(Object val) { - return withComparisonOperator(ComparisonOperator.LE).values(val); - } - - /** - * Creates and returns a condition of the range key being less than the - * given value. - */ - public T lt(Object val) { - return withComparisonOperator(ComparisonOperator.LT).values(val); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/GetItemImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/GetItemImpl.java deleted file mode 100644 index 3ea0bd0a9835..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/GetItemImpl.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.GetItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.api.GetItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.GetItemSpec; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; - -/** - * The implementation for GetItemApi. - */ -public class GetItemImpl extends AbstractImpl implements GetItemApi { - public GetItemImpl(DynamoDbClient client, Table table) { - super(client, table); - } - - @Override - public GetItemOutcome getItemOutcome(KeyAttribute... primaryKeyComponents) { - return doLoadItem(new GetItemSpec() - .withPrimaryKey(primaryKeyComponents)); - } - - @Override - public GetItemOutcome getItemOutcome(PrimaryKey primaryKey) { - return doLoadItem(new GetItemSpec() - .withPrimaryKey(primaryKey)); - } - - @Override - public GetItemOutcome getItemOutcome(PrimaryKey primaryKey, - String projectionExpression, Map nameMap) { - return doLoadItem(new GetItemSpec() - .withPrimaryKey(primaryKey) - .withProjectionExpression(projectionExpression) - .withNameMap(nameMap)); - } - - @Override - public GetItemOutcome getItemOutcome(GetItemSpec spec) { - return doLoadItem(spec); - } - - @Override - public Item getItem(GetItemSpec spec) { - return doLoadItem(spec).getItem(); - } - - private GetItemOutcome doLoadItem(GetItemSpec spec) { - String tableName = getTable().getTableName(); - // Set up the key attributes - GetItemRequest req = spec.getRequest().toBuilder() - .tableName(tableName) - .key(InternalUtils.toAttributeValueMap(spec.getKeyComponents())) - .expressionAttributeNames(spec.nameMap()) - .build(); - - GetItemResponse result = getClient().getItem(req); - return new GetItemOutcome(result); - } - - @Override - public Item getItem(KeyAttribute... primaryKey) { - return getItemOutcome(primaryKey).getItem(); - } - - @Override - public Item getItem(PrimaryKey primaryKey) { - return getItemOutcome(primaryKey).getItem(); - } - - @Override - public Item getItem(PrimaryKey primaryKey, String projectionExpression, - Map nameMap) { - return getItemOutcome(primaryKey, projectionExpression, nameMap).getItem(); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue) { - return getItemOutcome(new KeyAttribute(hashKeyName, hashKeyValue)); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return getItemOutcome(new KeyAttribute(hashKeyName, hashKeyValue), - new KeyAttribute(rangeKeyName, rangeKeyValue)); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue) { - return getItemOutcome(hashKeyName, hashKeyValue).getItem(); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return getItemOutcome(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue).getItem(); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String projectionExpression, Map nameMap) { - return getItemOutcome(new PrimaryKey(hashKeyName, hashKeyValue), - projectionExpression, nameMap); - } - - @Override - public GetItemOutcome getItemOutcome(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String projectionExpression, Map nameMap) { - return getItemOutcome( - new PrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue), - projectionExpression, nameMap); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue, - String projectionExpression, Map nameMap) { - return getItemOutcome(new PrimaryKey(hashKeyName, hashKeyValue), - projectionExpression, nameMap).getItem(); - } - - @Override - public Item getItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String projectionExpression, Map nameMap) { - return getItemOutcome( - new PrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue), - projectionExpression, nameMap).getItem(); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IndexQueryImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IndexQueryImpl.java deleted file mode 100644 index 11313cc5e192..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IndexQueryImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Index; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.QueryOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; - -/** - * The implementation for QueryApi for an index. - */ -public class IndexQueryImpl extends QueryImpl { - private final Index index; - - public IndexQueryImpl(DynamoDbClient client, Index index) { - super(client, index.getTable()); - this.index = index; - } - - @Override - protected ItemCollection doQuery(QuerySpec spec) { - spec.setRequest(spec.getRequest().toBuilder().indexName(index.getIndexName()).build()); - return super.doQuery(spec); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IndexScanImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IndexScanImpl.java deleted file mode 100644 index 225fc1d2766c..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IndexScanImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Index; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.ScanOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; - -/** - * The implementation for ScanApi for an index. - */ -public class IndexScanImpl extends ScanImpl { - private final Index index; - - public IndexScanImpl(DynamoDbClient client, Index index) { - super(client, index.getTable()); - this.index = index; - } - - @Override - protected ItemCollection doScan(ScanSpec spec) { - spec.setRequest(spec.getRequest().toBuilder().indexName(index.getIndexName()).build()); - return super.doScan(spec); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/InternalUtils.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/InternalUtils.java deleted file mode 100644 index 05459b48eb14..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/InternalUtils.java +++ /dev/null @@ -1,662 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static software.amazon.awssdk.utils.BinaryUtils.copyAllBytesFrom; - -import java.math.BigDecimal; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.awscore.AwsRequest; -import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.core.util.SdkAutoConstructList; -import software.amazon.awssdk.core.util.SdkAutoConstructMap; -import software.amazon.awssdk.core.util.VersionInfo; -import software.amazon.awssdk.services.dynamodb.document.AttributeUpdate; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.IncompatibleTypeException; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -/** - * Internal utilities. Not meant for general use. May change without notice. - */ -public final class InternalUtils { - - private InternalUtils() { - } - - /** - * Returns a non-null list of Item's given the low level - * list of item information. - */ - public static List toItemList(List> items) { - if (items == null) { - return Collections.emptyList(); - } - List result = new ArrayList(items.size()); - for (Map item : items) { - result.add(Item.fromMap(toSimpleMapValue(item))); - } - return result; - } - - /** - * Converts an Item into the low-level representation; - * or null if the input is null. - */ - public static Map toAttributeValues(Item item) { - if (item == null) { - return null; - } - // row with multiple attributes - Map result = new LinkedHashMap(); - for (Map.Entry entry : item.attributes()) { - result.put(entry.getKey(), toAttributeValue(entry.getValue())); - } - return result; - } - - /** - * Converts a map of string to simple objects into the low-level - * representation; or null if the input is null. - */ - public static Map fromSimpleMap( - Map map) { - if (map == null) { - return null; - } - // row with multiple attributes - Map result = new LinkedHashMap(); - for (Map.Entry entry : map.entrySet()) { - result.put(entry.getKey(), toAttributeValue(entry.getValue())); - } - return result; - } - - /** - * Converts a list of AttributeUpdate into the low-level - * representation; or null if the input is null. - */ - public static Map toAttributeValueUpdate( - List attributesToUpdate) { - if (attributesToUpdate == null) { - return null; - } - - Map result = new LinkedHashMap(); - - for (AttributeUpdate attribute : attributesToUpdate) { - AttributeValueUpdate.Builder attributeToUpdateBuilder = AttributeValueUpdate.builder() - .action(attribute.getAction()); - if (attribute.value() != null) { - attributeToUpdateBuilder.value(toAttributeValue(attribute.value())); - } else if (attribute.getAttributeValues() != null) { - attributeToUpdateBuilder.value(toAttributeValue(attribute - .getAttributeValues())); - } - result.put(attribute.getAttributeName(), attributeToUpdateBuilder.build()); - } - - return result; - } - - /** - * Converts a simple value into the low-level {@code } - * representation. - * - * @param value - * the given value which can be one of the followings: - *

      - *
    • String
    • - *
    • Set<String>
    • - *
    • Number (including any subtypes and primitive types)
    • - *
    • Set<Number>
    • - *
    • byte[]
    • - *
    • Set<byte[]>
    • - *
    • ByteBuffer
    • - *
    • Set<ByteBuffer>
    • - *
    • Boolean or boolean
    • - *
    • null
    • - *
    • Map<String,T>, where T can be any type on this list but must not - * induce any circular reference
    • - *
    • List<T>, where T can be any type on this list but must not induce - * any circular reference
    • - *
    - * @return a non-null low level representation of the input object value - * - * @throws UnsupportedOperationException - * if the input object type is not supported - */ - public static AttributeValue toAttributeValue(Object value) { - AttributeValue.Builder resultBuilder = AttributeValue.builder(); - if (value == null) { - return resultBuilder.nul(Boolean.TRUE).build(); - } else if (value instanceof Boolean) { - return resultBuilder.bool((Boolean) value).build(); - } else if (value instanceof String) { - return resultBuilder.s((String) value).build(); - } else if (value instanceof BigDecimal) { - BigDecimal bd = (BigDecimal) value; - return resultBuilder.n(bd.toPlainString()).build(); - } else if (value instanceof Number) { - return resultBuilder.n(value.toString()).build(); - } else if (value instanceof byte[]) { - return resultBuilder.b(SdkBytes.fromByteArray((byte[]) value)).build(); - } else if (value instanceof ByteBuffer) { - return resultBuilder.b(SdkBytes.fromByteBuffer((ByteBuffer) value)).build(); - } else if (value instanceof Set) { - // default to an empty string set if there is no element - @SuppressWarnings("unchecked") - Set set = (Set) value; - if (set.size() == 0) { - resultBuilder.ss(new ArrayList<>()); - return resultBuilder.build(); - } - Object element = set.iterator().next(); - if (element instanceof String) { - @SuppressWarnings("unchecked") - Set ss = (Set) value; - resultBuilder.ss(new ArrayList(ss)); - } else if (element instanceof Number) { - @SuppressWarnings("unchecked") - Set in = (Set) value; - List out = new ArrayList(set.size()); - for (Number n : in) { - BigDecimal bd = InternalUtils.toBigDecimal(n); - out.add(bd.toPlainString()); - } - resultBuilder.ns(out); - } else if (element instanceof byte[]) { - @SuppressWarnings("unchecked") - Set in = (Set) value; - List out = new ArrayList<>(set.size()); - for (byte[] buf : in) { - out.add(SdkBytes.fromByteArray(buf)); - } - resultBuilder.bs(out); - } else if (element instanceof ByteBuffer) { - @SuppressWarnings("unchecked") - Set in = (Set) value; - List out = new ArrayList<>(set.size()); - for (ByteBuffer buf : in) { - out.add(SdkBytes.fromByteBuffer(buf)); - } - resultBuilder.bs(out); - } else { - throw new UnsupportedOperationException("element type: " - + element.getClass()); - } - } else if (value instanceof List) { - @SuppressWarnings("unchecked") - List in = (List) value; - List out = new ArrayList(); - for (Object v : in) { - out.add(toAttributeValue(v)); - } - resultBuilder.l(out); - } else if (value instanceof Map) { - @SuppressWarnings("unchecked") - Map in = (Map) value; - Map attrs = new HashMap<>(); - for (Map.Entry e : in.entrySet()) { - attrs.put(e.getKey(), toAttributeValue(e.getValue())); - //resultBuilder.addMEntry(e.getKey(), toAttributeValue(e.getValue())); - } - resultBuilder.m(attrs); - } else { - throw new UnsupportedOperationException("value type: " - + value.getClass()); - } - return resultBuilder.build(); - } - - /** - * Converts a list of low-level AttributeValue into a list of - * simple values. Each value in the returned list can be one of the - * followings: - * - *
      - *
    • String
    • - *
    • Set<String>
    • - *
    • Number (including any subtypes and primitive types)
    • - *
    • Set<Number>
    • - *
    • byte[]
    • - *
    • Set<byte[]>
    • - *
    • ByteBuffer
    • - *
    • Set<ByteBuffer>
    • - *
    • Boolean or boolean
    • - *
    • null
    • - *
    • Map<String,T>, where T can be any type on this list but must not - * induce any circular reference
    • - *
    • List<T>, where T can be any type on this list but must not induce - * any circular reference
    • - *
    - */ - public static List toSimpleList(List attrValues) { - if (attrValues == null) { - return null; - } - List result = new ArrayList(attrValues.size()); - for (AttributeValue attrValue : attrValues) { - Object value = toSimpleValue(attrValue); - result.add(value); - } - return result; - } - - /** - * Convenient method to convert a list of low-level - * AttributeValue into a list of values of the same type T. - * Each value in the returned list can be one of the followings: - *
      - *
    • String
    • - *
    • Set<String>
    • - *
    • Number (including any subtypes and primitive types)
    • - *
    • Set<Number>
    • - *
    • byte[]
    • - *
    • Set<byte[]>
    • - *
    • ByteBuffer
    • - *
    • Set<ByteBuffer>
    • - *
    • Boolean or boolean
    • - *
    • null
    • - *
    • Map<String,T>, where T can be any type on this list but must not - * induce any circular reference
    • - *
    • List<T>, where T can be any type on this list but must not induce - * any circular reference
    • - *
    - */ - public static List toSimpleListValue(List values) { - if (values == null) { - return null; - } - - List result = new ArrayList(values.size()); - for (AttributeValue v : values) { - T t = toSimpleValue(v); - result.add(t); - } - return result; - } - - public static Map toSimpleMapValue( - Map values) { - if (values == null) { - return null; - } - - Map result = new LinkedHashMap(values.size()); - for (Map.Entry entry : values.entrySet()) { - T t = toSimpleValue(entry.getValue()); - result.put(entry.getKey(), t); - } - return result; - } - - /** - * Returns the string representation of the given value; or null if the - * value is null. For BigDecimal it will be the string - * representation without an exponent field. - */ - public static String valToString(Object val) { - if (val instanceof BigDecimal) { - BigDecimal bd = (BigDecimal) val; - return bd.toPlainString(); - } - if (val == null) { - return null; - } - if (val instanceof String - || val instanceof Boolean - || val instanceof Number) { - return val.toString(); - } - throw new IncompatibleTypeException("Cannot convert " + val.getClass() + " into a string"); - } - - /** - * Converts a low-level AttributeValue into a simple value, - * which can be one of the followings: - * - *
      - *
    • String
    • - *
    • Set<String>
    • - *
    • Number (including any subtypes and primitive types)
    • - *
    • Set<Number>
    • - *
    • byte[]
    • - *
    • Set<byte[]>
    • - *
    • ByteBuffer
    • - *
    • Set<ByteBuffer>
    • - *
    • Boolean or boolean
    • - *
    • null
    • - *
    • Map<String,T>, where T can be any type on this list but must not - * induce any circular reference
    • - *
    • List<T>, where T can be any type on this list but must not induce - * any circular reference
    • - *
    - * - * @throws IllegalArgumentException - * if an empty AttributeValue value is specified - */ - static T toSimpleValue(AttributeValue value) { - if (value == null) { - return null; - } - if (Boolean.TRUE.equals(value.nul())) { - return null; - } else if (Boolean.FALSE.equals(value.nul())) { - throw new UnsupportedOperationException("False-NULL is not supported in DynamoDB"); - } else if (value.bool() != null) { - @SuppressWarnings("unchecked") - T t = (T) value.bool(); - return t; - } else if (value.s() != null) { - @SuppressWarnings("unchecked") - T t = (T) value.s(); - return t; - } else if (value.n() != null) { - @SuppressWarnings("unchecked") - T t = (T) new BigDecimal(value.n()); - return t; - } else if (value.b() != null) { - @SuppressWarnings("unchecked") - T t = (T) value.b().asByteArray(); - return t; - } else if (value.ss() != null && !(value.ss() instanceof SdkAutoConstructList)) { - @SuppressWarnings("unchecked") - T t = (T) new LinkedHashSet(value.ss()); - return t; - } else if (value.ns() != null && !(value.ns() instanceof SdkAutoConstructList)) { - Set set = new LinkedHashSet(value.ns().size()); - for (String s : value.ns()) { - set.add(new BigDecimal(s)); - } - @SuppressWarnings("unchecked") - T t = (T) set; - return t; - } else if (value.bs() != null && !(value.bs() instanceof SdkAutoConstructList)) { - Set set = new LinkedHashSet(value.bs().size()); - for (SdkBytes bb : value.bs()) { - set.add(copyAllBytesFrom(bb.asByteBuffer())); - } - @SuppressWarnings("unchecked") - T t = (T) set; - return t; - } else if (value.l() != null && !(value.l() instanceof SdkAutoConstructList)) { - @SuppressWarnings("unchecked") - T t = (T) toSimpleList(value.l()); - return t; - } else if (value.m() != null && !(value.m() instanceof SdkAutoConstructMap)) { - @SuppressWarnings("unchecked") - T t = (T) toSimpleMapValue(value.m()); - return t; - } else { - throw new IllegalArgumentException( - "Attribute value must not be empty: " + value); - } - } - - /** - * Returns the minimum of the two input integers taking null into account. - * Returns null if both integers are null. Otherwise, a null Integer is - * treated as infinity. - */ - public static Integer minimum(Integer one, Integer two) { - if (one == null) { - return two; - } else if (two == null) { - return one; - } else if (one < two) { - return one; - } else { - return two; - } - } - - /** - * Returns the low level representation of a collection of Expected. - */ - public static Map toExpectedAttributeValueMap( - Collection expectedSet) { - if (expectedSet == null) { - return null; - } - Map expectedMap = - new LinkedHashMap(); - for (Expected expected : expectedSet) { - final String attr = expected.getAttribute(); - final Object[] values = expected.values(); - ExpectedAttributeValue.Builder eavBuilder = ExpectedAttributeValue.builder(); - if (values != null) { - if (values.length > 0) { - // convert from list of object values to list of AttributeValues - AttributeValue[] avs = InternalUtils.toAttributeValues(values); - eavBuilder.attributeValueList(avs); - } else { - throw new IllegalStateException("Bug!"); - } - } - ComparisonOperator op = expected.getComparisonOperator(); - if (op == null) { - throw new IllegalArgumentException( - "Comparison operator for attribute " + expected.getAttribute() - + " must be specified"); - } - eavBuilder.comparisonOperator(op); - expectedMap.put(attr, eavBuilder.build()); - } - if (expectedSet.size() != expectedMap.size()) { - throw new IllegalArgumentException("duplicates attribute names not allowed in input"); - } - return Collections.unmodifiableMap(expectedMap); - } - - /** - * Returns the low level representation of a collection of Filter. - */ - public static Map toAttributeConditionMap(Collection> filters) { - if (filters == null) { - return null; - } - Map conditionMap = new LinkedHashMap(); - for (Filter filter : filters) { - final String attr = filter.getAttribute(); - final Object[] values = filter.values(); - Condition.Builder conditionBuilder = Condition.builder(); - if (values != null) { - if (values.length > 0) { - // convert from list of object values to list of AttributeValues - AttributeValue[] avs = InternalUtils.toAttributeValues(values); - conditionBuilder.attributeValueList(avs); - } else { - throw new IllegalStateException("Bug!"); - } - } - ComparisonOperator op = filter.getComparisonOperator(); - if (op == null) { - throw new IllegalArgumentException( - "Comparison operator for attribute " + filter.getAttribute() - + " must be specified"); - } - conditionBuilder.comparisonOperator(op); - conditionMap.put(attr, conditionBuilder.build()); - } - if (filters.size() != conditionMap.size()) { - throw new IllegalArgumentException("duplicates attribute names not allowed in input"); - } - return Collections.unmodifiableMap(conditionMap); - } - - /** - * Converts the input array of values into an array of low level - * representation of those values. - * - * A value in the input array can be one of the followings: - * - *
      - *
    • String
    • - *
    • Set<String>
    • - *
    • Number (including any subtypes and primitive types)
    • - *
    • Set<Number>
    • - *
    • byte[]
    • - *
    • Set<byte[]>
    • - *
    • ByteBuffer
    • - *
    • Set<ByteBuffer>
    • - *
    • Boolean or boolean
    • - *
    • null
    • - *
    • Map<String,T>, where T can be any type on this list but must not - * induce any circular reference
    • - *
    • List<T>, where T can be any type on this list but must not induce - * any circular reference
    • - *
    - */ - public static AttributeValue[] toAttributeValues(Object[] values) { - AttributeValue[] attrValues = new AttributeValue[values.length]; - for (int i = 0; i < values.length; i++) { - attrValues[i] = InternalUtils.toAttributeValue(values[i]); - } - return attrValues; - } - - /** - * Converts the specified primary key into the low-level representation. - */ - public static Map toAttributeValueMap( - Collection primaryKey) { - if (primaryKey == null) { - return null; - } - Map keys = new LinkedHashMap(); - for (KeyAttribute keyAttr : primaryKey) { - keys.put(keyAttr.name(), - InternalUtils.toAttributeValue(keyAttr.value())); - } - return Collections.unmodifiableMap(keys); - } - - /** - * Converts the specified primary key into the low-level representation. - */ - public static Map toAttributeValueMap( - PrimaryKey primaryKey) { - if (primaryKey == null) { - return null; - } - return toAttributeValueMap(primaryKey.getComponents()); - } - - /** - * Converts the specified primary key into the low-level representation. - */ - public static Map toAttributeValueMap( - KeyAttribute... primaryKey) { - if (primaryKey == null) { - return null; - } - return toAttributeValueMap(Arrays.asList(primaryKey)); - } - - /** - * Converts a number into BigDecimal representation. - */ - public static BigDecimal toBigDecimal(Number n) { - if (n instanceof BigDecimal) { - return (BigDecimal) n; - } - return new BigDecimal(n.toString()); - } - - public static Set toBigDecimalSet(Number... val) { - Set set = new LinkedHashSet(val.length); - for (Number n : val) { - set.add(InternalUtils.toBigDecimal(n)); - } - return set; - } - - public static Set toBigDecimalSet(Set vals) { - Set set = new LinkedHashSet(vals.size()); - for (Number n : vals) { - set.add(InternalUtils.toBigDecimal(n)); - } - return set; - } - - /** - * Append the custom user-agent string. - */ - public static X applyUserAgent(X request) { - final AwsRequestOverrideConfiguration newCfg = request.overrideConfiguration() - .map(AwsRequestOverrideConfiguration::toBuilder) - .orElse(AwsRequestOverrideConfiguration.builder()) - .addApiName(apiName -> apiName.name("dynamodb-table-api").version(VersionInfo.SDK_VERSION)) - .build(); - - return (X) request.toBuilder() - .overrideConfiguration(newCfg) - .build(); - } - - public static void rejectNullValue(Object val) { - if (val == null) { - throw new IllegalArgumentException("Input value must not be null"); - } - } - - public static void rejectNullInput(Object input) { - if (input == null) { - throw new IllegalArgumentException("Input must not be null"); - } - } - - public static void rejectEmptyInput(Object[] input) { - if (input.length == 0) { - throw new IllegalArgumentException("At least one input must be specified"); - } - } - - public static void rejectNullOrEmptyInput(Object[] input) { - rejectNullInput(input); - rejectEmptyInput(input); - } - - public static void checkInvalidAttrName(String attrName) { - if (attrName == null || attrName.trim().length() == 0) { - throw new IllegalArgumentException("Attribute name must not be null or empty"); - } - } - - public static void checkInvalidAttribute(String attrName, Object val) { - checkInvalidAttrName(attrName); - rejectNullValue(val); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/InternalUtilsTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/InternalUtilsTest.java deleted file mode 100644 index 40917e2f6d98..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/InternalUtilsTest.java +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toAttributeValues; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toItemList; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toSimpleList; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toSimpleValue; -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.valToString; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.core.util.SdkAutoConstructList; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.utils.FluentHashSet; -import software.amazon.awssdk.services.dynamodb.document.utils.ValueMap; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -public class InternalUtilsTest { - - @Test - public void nullInput() { - assertTrue(toItemList(null).size() == 0); - assertNull(toAttributeValues((Item) null)); - assertNull(toSimpleList(null)); - assertNull(toSimpleValue(null)); - assertNull(valToString(null)); - } - - @Test - @Ignore // Does not pass anymore because the builder will create a duplicate of the BB - public void toAttributeValue_ByteBuffer() { - ByteBuffer bbFrom = ByteBuffer.allocate(10); - AttributeValue av = InternalUtils.toAttributeValue(bbFrom); - ByteBuffer bbTo = av.b().asByteBuffer(); - assertSame(bbFrom, bbTo); - } - - @Test - public void toAttributeValue_byteArray() { - byte[] bytesFrom = {1, 2, 3, 4}; - AttributeValue av = InternalUtils.toAttributeValue(bytesFrom); - ByteBuffer bbTo = av.b().asByteBuffer(); - assertTrue(ByteBuffer.wrap(bytesFrom).compareTo(bbTo) == 0); - } - - @Test - public void toAttributeValue_Number() { - { - AttributeValue av = InternalUtils.toAttributeValue(123); - String num = av.n(); - assertEquals("123", num); - } - { // 17 decimal places - AttributeValue av = InternalUtils.toAttributeValue(0.99999999999999999); - String num = av.n(); - assertEquals("1.0", num); - } - { // 16 decimal places - AttributeValue av = InternalUtils.toAttributeValue(0.9999999999999999); - String num = av.n(); - assertEquals("0.9999999999999999", num); - } - { - String numFrom = "0.99999999999999999999999999999999999999"; - AttributeValue av = InternalUtils.toAttributeValue( - new BigDecimal(numFrom)); - String numTo = av.n(); - assertEquals(numFrom, numTo); - } - } - - @Test - public void toAttributeValue_emptySet() { - AttributeValue av = InternalUtils.toAttributeValue(new HashSet()); - List ss = av.ss(); - assertTrue(ss.size() == 0); - assertTrue(av.ns() instanceof SdkAutoConstructList); - } - - @Test - public void toAttributeValue_NumberSet() { - Set nsFrom = new FluentHashSet() - .with(123) - .with(123.45) - .with(Integer.valueOf(678)) - .with(new BigInteger("1234567890123456789012345678901234567890")) - .with(new BigDecimal("0.99999999999999999999999999999999999999")); - AttributeValue av = InternalUtils.toAttributeValue(nsFrom); - assertTrue(av.ss() instanceof SdkAutoConstructList); - List ns = av.ns(); - assertTrue(ns.size() == 5); - assertTrue(ns.contains("123")); - assertTrue(ns.contains("123.45")); - assertTrue(ns.contains("678")); - assertTrue(ns.contains("1234567890123456789012345678901234567890")); - assertTrue(ns.contains("0.99999999999999999999999999999999999999")); - } - - @Test - public void toAttributeValue_ByteArraySet() { - byte[] ba1From = new byte[] {1, 2, 3}; - byte[] ba2From = new byte[] {4, 5, 6}; - Set nsFrom = new FluentHashSet() - .with(ba1From) - .with(ba2From); - AttributeValue av = InternalUtils.toAttributeValue(nsFrom); - assertTrue(av.ss() instanceof SdkAutoConstructList); - List bs = av.bs(); - assertTrue(bs.size() == 2); - boolean bool1 = false; - boolean bool2 = false; - for (SdkBytes b : bs) { - if (ByteBuffer.wrap(ba1From).compareTo(b.asByteBuffer()) == 0) { - bool1 = true; - } else if (ByteBuffer.wrap(ba2From).compareTo(b.asByteBuffer()) == 0) { - bool2 = true; - } - } - assertTrue(bool1); - assertTrue(bool2); - } - - @Test - public void toAttributeValue_ByteBufferSet() { - byte[] ba1From = new byte[] {1, 2, 3}; - byte[] ba2From = new byte[] {4, 5, 6}; - Set nsFrom = new FluentHashSet() - .with(ByteBuffer.wrap(ba1From)) - .with(ByteBuffer.wrap(ba2From)); - AttributeValue av = InternalUtils.toAttributeValue(nsFrom); - assertTrue(av.ss() instanceof SdkAutoConstructList); - List bs = av.bs(); - assertTrue(bs.size() == 2); - boolean bool1 = false; - boolean bool2 = false; - for (SdkBytes b : bs) { - if (ByteBuffer.wrap(ba1From).compareTo(b.asByteBuffer()) == 0) { - bool1 = true; - } else if (ByteBuffer.wrap(ba2From).compareTo(b.asByteBuffer()) == 0) { - bool2 = true; - } - } - assertTrue(bool1); - assertTrue(bool2); - } - - @Test - public void toAttributeValue_null() { - AttributeValue av = InternalUtils.toAttributeValue(null); - assertEquals(Boolean.TRUE, av.nul()); - } - - @Test(expected = UnsupportedOperationException.class) - public void toAttributeValue_UnsupportedOperationException() { - InternalUtils.toAttributeValue(new Object()); - } - - @Test - public void toAttributeValue_emptyMap() { - AttributeValue av = InternalUtils.toAttributeValue(new HashMap()); - Map m = av.m(); - assertTrue(m.size() == 0); - } - - @Test - public void toAttributeValue_emptyList() { - AttributeValue av = InternalUtils.toAttributeValue(new ArrayList()); - List l = av.l(); - assertTrue(l.size() == 0); - } - - @Test - public void toAttributeValue_MapOfMap() { - AttributeValue av = InternalUtils.toAttributeValue(new ValueMap() - .with("emptyMap", new ValueMap())); - Map m = av.m(); - assertTrue(m.size() == 1); - AttributeValue emptyMap = m.get("emptyMap"); - Map mInner = emptyMap.m(); - assertTrue(0 == mInner.size()); - } - - @Test - public void toSimpleListValue_empty() { - List listFrom = new ArrayList(); - List listTo = toSimpleList(listFrom); - assertTrue(listTo.size() == 0); - } - - @Test - public void toSimpleListValue_null() { - assertNull(InternalUtils.toSimpleListValue(null)); - } - - @Test - public void toSimpleListValue() { - List listFrom = new ArrayList(); - listFrom.add(AttributeValue.builder().s("test").build()); - listFrom.add(AttributeValue.builder().n("123").build()); - List listTo = InternalUtils.toSimpleListValue(listFrom); - assertTrue(listTo.size() == 2); - assertEquals("test", listTo.get(0)); - assertEquals(new BigDecimal("123"), listTo.get(1)); - } - - @Test - public void toSimpleValue_null() { - assertNull(toSimpleValue(null)); - assertNull(toSimpleValue(AttributeValue.builder().nul(Boolean.TRUE).build())); - } - - @Test(expected = IllegalArgumentException.class) - public void toSimpleValue_empty() { - toSimpleValue(AttributeValue.builder().build()); - } - - @Test(expected = UnsupportedOperationException.class) - public void toSimpleValue_FalseNull() { - toSimpleValue(AttributeValue.builder().nul(Boolean.FALSE).build()); - } - - @Test - public void toSimpleValue_NS() { - Set numset = toSimpleValue( - AttributeValue.builder().ns("123", "456").build()); - assertTrue(numset.size() == 2); - assertTrue(numset.contains(new BigDecimal("123"))); - assertTrue(numset.contains(new BigDecimal("456"))); - } - - @Test - public void toSimpleValue_emptyNS() { - Set numset = toSimpleValue( - AttributeValue.builder().ns(new ArrayList()).build()); - assertTrue(numset.size() == 0); - } - - @Test - public void toSimpleValue_M() { - Map mapFrom = new HashMap(); - mapFrom.put("fooBOOL", AttributeValue.builder().bool(Boolean.TRUE).build()); - mapFrom.put("fooString", AttributeValue.builder().s("bar").build()); - Map mapTo = toSimpleValue( - AttributeValue.builder().m(mapFrom).build()); - assertTrue(mapTo.size() == 2); - assertEquals(Boolean.TRUE, mapTo.get("fooBOOL")); - assertEquals("bar", mapTo.get("fooString")); - } - - @Test - public void toSimpleValue_emptyM() { - Map mapFrom = new HashMap(); - Map mapTo = toSimpleValue( - AttributeValue.builder().m(mapFrom).build()); - assertTrue(mapTo.size() == 0); - } - - @Test - public void toSimpleValue_ByteArray() { - byte[] bytesFrom = new byte[] {1, 2, 3}; - ByteBuffer byteBufferTo = ByteBuffer.allocate(3).put(bytesFrom); - byteBufferTo.rewind(); - byte[] bytesTo = toSimpleValue( - AttributeValue.builder().b(SdkBytes.fromByteBuffer(byteBufferTo)).build()); - assertTrue(Arrays.equals(bytesTo, bytesFrom)); - } - - @Test - public void toSimpleValue_DirectByteBuffer() { - byte[] bytesFrom = new byte[] {1, 2, 3}; - ByteBuffer byteBufferTo = ByteBuffer.allocateDirect(3).put(bytesFrom); - byteBufferTo.rewind(); - byte[] bytesTo = toSimpleValue( - AttributeValue.builder().b(SdkBytes.fromByteBuffer(byteBufferTo)).build()); - assertTrue(Arrays.equals(bytesTo, bytesFrom)); - } - - @Test(expected = IllegalArgumentException.class) - public void toExpectedAttributeValueMap_missingComparisonOperator() { - InternalUtils.toExpectedAttributeValueMap(Arrays.asList(new Expected("attrName"))); - } - - @Test - public void toExpectedAttributeValueMap() { - Map to = - InternalUtils.toExpectedAttributeValueMap(Arrays.asList( - new Expected("attr1").exists(), - new Expected("attr2").exists() - )); - assertTrue(to.size() == 2); - } - - @Test(expected = IllegalArgumentException.class) - public void toExpectedAttributeValueMap_duplicateAttributeNames() { - InternalUtils.toExpectedAttributeValueMap(Arrays.asList( - new Expected("attr1").exists(), - new Expected("attr1").ge(1) - )); - } - - @Test - public void toAttributeValueMap_nullKeyAttributeCollection() { - assertNull(InternalUtils.toAttributeValueMap((Collection) null)); - } - - @Test - public void toAttributeValueMap_nullPrimaryKey() { - assertNull(InternalUtils.toAttributeValueMap((PrimaryKey) null)); - } - - @Test - public void toAttributeValueMap_nullKeyAttributes() { - assertNull(InternalUtils.toAttributeValueMap((KeyAttribute[]) null)); - } - - @Test - public void toAttributeValueMap_KeyAttributes() { - Map map = InternalUtils.toAttributeValueMap( - new KeyAttribute("hashname", "hashvalue"), - new KeyAttribute("rangekey", 123)); - AttributeValue av = map.get("hashname"); - assertEquals("hashvalue", av.s()); - av = map.get("rangekey"); - assertEquals("123", av.n()); - } - - @Test - public void valToString_int() { - String s = valToString(123.456); - assertEquals("123.456", s); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ItemValueConformer.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ItemValueConformer.java deleted file mode 100644 index 5d76d8c32fc7..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ItemValueConformer.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static software.amazon.awssdk.utils.BinaryUtils.copyBytesFrom; - -import java.math.BigDecimal; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Used to standardize a given Item's value into a standard - * internal representation (for purposes such as equality comparison.) - */ -public class ItemValueConformer extends ValueTransformer { - /** - * This method is assumed to be called for the purpose of a setter method - * invocation, but NOT a getter method invocation. - */ - @Override - public Object transform(Object value) { - if (value == null) { - return value; - } else if (value instanceof Boolean) { - return value; - } else if (value instanceof String) { - return value; - } else if (value instanceof Number) { - return InternalUtils.toBigDecimal((Number) value); - } else if (value instanceof byte[]) { - return value; - } else if (value instanceof ByteBuffer) { - return copyBytesFrom((ByteBuffer) value); - } else if (value instanceof Set) { - @SuppressWarnings("unchecked") - Set set = (Set) value; - if (set.size() == 0) { - return value; - } - Object element = set.iterator().next(); - if (element instanceof String) { - return value; - } else if (element instanceof BigDecimal) { - return value; - } else if (element instanceof Number) { - @SuppressWarnings("unchecked") - Set in = (Set) value; - Set out = new LinkedHashSet(set.size()); - for (Number n : in) { - out.add(InternalUtils.toBigDecimal(n)); - } - return out; - } else if (element instanceof byte[]) { - return value; - } else if (element instanceof ByteBuffer) { - @SuppressWarnings("unchecked") - Set bs = (Set) value; - Set out = new LinkedHashSet(bs.size()); - for (ByteBuffer bb : bs) { - out.add(copyBytesFrom(bb)); - } - return out; - } else { - throw new UnsupportedOperationException("element type: " - + element.getClass()); - } - } else if (value instanceof List) { - @SuppressWarnings("unchecked") - List in = (List) value; - if (in.size() == 0) { - return in; - } - List out = new ArrayList(); - for (Object v : in) { - out.add(transform(v)); - } - return out; - } else if (value instanceof Map) { - @SuppressWarnings("unchecked") - Map in = (Map) value; - if (in.size() == 0) { - return in; - } - Map out = new LinkedHashMap(in.size()); - for (Map.Entry e : in.entrySet()) { - out.put(e.getKey(), transform(e.getValue())); - } - return out; - } else { - throw new UnsupportedOperationException("value type: " - + value.getClass()); - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ItemValueConformerTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ItemValueConformerTest.java deleted file mode 100644 index 81bb2de283e1..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ItemValueConformerTest.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.math.BigDecimal; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.document.utils.FluentHashSet; - -public class ItemValueConformerTest { - - @Test - public void byteBuffer() { - byte[] bytes = {1, 2, 3}; - ByteBuffer bb = ByteBuffer.wrap(bytes); - byte[] bytesTo = (byte[]) new ItemValueConformer().transform(bb); - assertTrue(Arrays.equals(bytesTo, bytes)); - } - - @Test - public void emptySet() { - Set from = new HashSet(); - Set to = (Set) new ItemValueConformer().transform(from); - assertTrue(to.size() == 0); - } - - @Test - public void stringSet() { - Set from = new FluentHashSet("a", "b"); - Set to = (Set) new ItemValueConformer().transform(from); - assertTrue(to.size() == 2); - assertTrue(to.contains("a")); - assertTrue(to.contains("b")); - } - - @Test - public void bytesSet() { - byte[] bytes123 = {1, 2, 3}; - byte[] bytes456 = {4, 5, 6}; - Set from = new FluentHashSet(bytes123, bytes456); - @SuppressWarnings("unchecked") - Set to = (Set) new ItemValueConformer().transform(from); - assertTrue(to.size() == 2); - boolean a = false, b = false; - for (byte[] bytes : to) { - if (Arrays.equals(bytes123, bytes)) { - a = true; - } else if (Arrays.equals(bytes456, bytes)) { - b = true; - } - } - assertTrue(a); - assertTrue(b); - } - - @Test - public void byteBufferSet() { - byte[] bytes123 = {1, 2, 3}; - byte[] bytes456 = {4, 5, 6}; - Set from = new FluentHashSet(ByteBuffer.wrap(bytes123), ByteBuffer.wrap(bytes456)); - @SuppressWarnings("unchecked") - Set to = (Set) new ItemValueConformer().transform(from); - assertTrue(to.size() == 2); - boolean a = false, b = false; - for (byte[] bytes : to) { - if (Arrays.equals(bytes123, bytes)) { - a = true; - } else if (Arrays.equals(bytes456, bytes)) { - b = true; - } - } - assertTrue(a); - assertTrue(b); - } - - @Test - public void bigDecimalSet() { - Set from = new FluentHashSet(BigDecimal.ZERO, BigDecimal.TEN); - Set to = (Set) new ItemValueConformer().transform(from); - assertTrue(to.size() == 2); - assertTrue(to.contains(BigDecimal.ZERO)); - assertTrue(to.contains(BigDecimal.TEN)); - } - - @Test - public void bigNumberSet() { - Set from = new FluentHashSet(Integer.MAX_VALUE, Double.MAX_VALUE); - Set to = (Set) new ItemValueConformer().transform(from); - assertTrue(to.size() == 2); - - assertFalse(to.contains(Integer.MAX_VALUE)); - assertFalse(to.contains(Double.MAX_VALUE)); - - assertTrue(to.contains(new BigDecimal(String.valueOf(Integer.MAX_VALUE)))); - assertTrue(to.contains(new BigDecimal(String.valueOf(Double.MAX_VALUE)))); - } - - @Test - public void emptyMap() { - Map from = new HashMap(); - Map to = (Map) new ItemValueConformer().transform(from); - assertTrue(to.size() == 0); - } - - @Test(expected = UnsupportedOperationException.class) - public void uknownType() { - new ItemValueConformer().transform(new Object()); - } - - @Test(expected = UnsupportedOperationException.class) - public void uknownsetType() { - new ItemValueConformer().transform(new FluentHashSet(new Object())); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IteratorSupport.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IteratorSupport.java deleted file mode 100644 index b84bb37c67b1..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/IteratorSupport.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Iterator; -import java.util.NoSuchElementException; -import software.amazon.awssdk.services.dynamodb.document.Page; - -/** - * An internal iterator implementation for {@link PageBasedCollection}. - *

    - * NOTE: this internal class is marked as public since it has been incorrectly - * exposed in the public method {@link PageBasedCollection#iterator()}, and it - * will be changed to be package private in the next major version. - * - * @param - * resource type - * @param - * low level result type - */ -public class IteratorSupport implements Iterator { - /** - * Used to iterate through the resource pages, dynamically making network - * calls as needed. - */ - final PageIterator resourcePageIterator; - /** - * Used to iterate through a list of resources already retrieved. - */ - private Iterator localResourceIterator; - private T resource; - - IteratorSupport(PageIterator resourcePageIterator) { - this.resourcePageIterator = resourcePageIterator; - } - - @Override - public boolean hasNext() { - if (resource != null) { - return true; - } - resource = nextResource(); - return (resource != null); - } - - @Override - public T next() { - T rval = resource; - if (rval == null) { - rval = nextResource(); - if (rval == null) { - throw new NoSuchElementException("No more elements"); - } - } else { - resource = null; - } - return rval; - } - - @Override - public void remove() { - throw new UnsupportedOperationException( - "Collection is read-only"); - } - - private T nextResource() { - while (true) { - if (localResourceIterator != null && localResourceIterator.hasNext()) { - return localResourceIterator.next(); - } - if (!resourcePageIterator.hasNext()) { - return null; - } - Page resourcePage = resourcePageIterator.next(); - localResourceIterator = resourcePage.iterator(); - } - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesCollection.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesCollection.java deleted file mode 100644 index 6f91c0312ac1..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesCollection.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Page; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.TableCollection; -import software.amazon.awssdk.services.dynamodb.document.spec.ListTablesSpec; -import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; - -class ListTablesCollection extends TableCollection { - - private final DynamoDbClient client; - private final ListTablesSpec spec; - private final String startKey; - - ListTablesCollection(DynamoDbClient client, ListTablesSpec spec) { - this.client = client; - this.spec = spec; - this.startKey = spec.getExclusiveStartTableName(); - } - - @Override - public Page firstPage() { - ListTablesRequest request = spec.getRequest() - .toBuilder() - .exclusiveStartTableName(startKey) - .limit(InternalUtils.minimum( - spec.maxResultSize(), - spec.maxPageSize())) - .build(); - spec.setRequest(request); - ListTablesResponse result = client.listTables(request); - setLastLowLevelResult(result); - return new ListTablesPage(client, spec, request, 0, result); - } - - @Override - public Integer getMaxResultSize() { - return spec.maxResultSize(); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesImpl.java deleted file mode 100644 index 9c488233e6e3..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.TableCollection; -import software.amazon.awssdk.services.dynamodb.document.api.ListTablesApi; -import software.amazon.awssdk.services.dynamodb.document.spec.ListTablesSpec; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; - -/** - * The implementation for ListTablesApi. - */ -public class ListTablesImpl implements ListTablesApi { - private final DynamoDbClient client; - - public ListTablesImpl(DynamoDbClient client) { - this.client = client; - } - - @Override - public TableCollection listTables(ListTablesSpec spec) { - return doList(spec); - } - - @Override - public TableCollection listTables() { - return doList(new ListTablesSpec()); - } - - @Override - public TableCollection listTables(String exclusiveStartTableName) { - return doList(new ListTablesSpec() - .withExclusiveStartTableName(exclusiveStartTableName)); - } - - @Override - public TableCollection listTables(String exclusiveStartTableName, - int maxResultSize) { - return doList(new ListTablesSpec() - .withExclusiveStartTableName(exclusiveStartTableName) - .withMaxResultSize(maxResultSize)); - } - - @Override - public TableCollection listTables(int maxResultSize) { - return doList(new ListTablesSpec() - .withMaxResultSize(maxResultSize)); - } - - private TableCollection doList(ListTablesSpec spec) { - return new ListTablesCollection(client, spec); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesPage.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesPage.java deleted file mode 100644 index 0e08ecd951cf..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ListTablesPage.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.NoSuchElementException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Page; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.spec.ListTablesSpec; -import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; - -class ListTablesPage extends Page { - private final DynamoDbClient client; - private final ListTablesSpec spec; - private ListTablesRequest request; - private final int index; - private final String lastEvaluatedKey; - - ListTablesPage( - DynamoDbClient client, - ListTablesSpec spec, - ListTablesRequest request, - int index, - ListTablesResponse result) { - super(Collections.unmodifiableList( - toTableList(client, result.tableNames())), - result); - this.client = client; - this.spec = spec; - this.request = request; - this.index = index; - Integer max = spec.maxResultSize(); - if (max != null && (index + result.tableNames().size()) > max) { - this.lastEvaluatedKey = null; - } else { - this.lastEvaluatedKey = result.lastEvaluatedTableName(); - } - } - - private static List toTableList(DynamoDbClient client, List tableNames) { - if (tableNames == null) { - return null; - } - List
    result = new ArrayList
    (tableNames.size()); - for (String tableName : tableNames) { - result.add(new Table(client, tableName)); - } - return result; - } - - @Override - public boolean hasNextPage() { - if (lastEvaluatedKey == null) { - return false; - } - Integer max = spec.maxResultSize(); - if (max == null) { - return true; - } - return nextRequestLimit(max.intValue()) > 0; - } - - private int nextRequestLimit(int max) { - int nextIndex = index + this.size(); - return InternalUtils.minimum( - max - nextIndex, - spec.maxPageSize()); - } - - @Override - public Page nextPage() { - if (lastEvaluatedKey == null) { - throw new NoSuchElementException("No more pages"); - } - final Integer max = spec.maxResultSize(); - if (max != null) { - int nextLimit = nextRequestLimit(max.intValue()); - if (nextLimit == 0) { - throw new NoSuchElementException("No more pages"); - } - request = request.toBuilder().limit(nextLimit).build(); - } - request = request.toBuilder().exclusiveStartTableName(lastEvaluatedKey).build(); - ListTablesResponse result = client.listTables(request); - final int nextIndex = index + this.size(); - return new ListTablesPage(client, spec, request, nextIndex, result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageBasedCollection.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageBasedCollection.java deleted file mode 100644 index 96c2380056b8..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageBasedCollection.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.document.LowLevelResultListener; -import software.amazon.awssdk.services.dynamodb.document.Page; - -/** - * Abstract base class for all page-based collections. - * - * @param resource type - * @param low level outcome/result type - */ -public abstract class PageBasedCollection implements Iterable { - private volatile R lastLowLevelResult; - private volatile LowLevelResultListener listener = LowLevelResultListener.none(); - - @Override - public IteratorSupport iterator() { - PageIterable pageIterable = pages(); - final PageIterator pageIterator = pageIterable.iterator(); - return new IteratorSupport(pageIterator); - } - - public PageIterable pages() { - return new PageIterable(this); - } - - public abstract Page firstPage(); - - /** - * Returns the maximum number of resources to be retrieved in this - * collection; or null if there is no limit. - */ - public abstract Integer getMaxResultSize(); - - /** - * Returns the low-level result last retrieved (for the current page) from - * the server side; or null if there has yet no calls to the server. - */ - public R getLastLowLevelResult() { - return lastLowLevelResult; - } - - /** - * Internal method used by the implementation layer for setting - * the low level result received from the server side. - */ - protected void setLastLowLevelResult(R lowLevelResult) { - this.lastLowLevelResult = lowLevelResult; - // deliver the event of receiving a low level result from the server side - listener.onLowLevelResult(lowLevelResult); - } - - /** - * Used to register a listener for the event of receiving a low-level result - * from the server side. - * - * @param listener - * listener to be registered. If null, a "none" listener will be - * set. - * @return the previously registered listener. The return value is never - * null. - */ - public LowLevelResultListener registerLowLevelResultListener(LowLevelResultListener listener) { - LowLevelResultListener prev = this.listener; - if (listener == null) { - this.listener = LowLevelResultListener.none(); - } else { - this.listener = listener; - } - return prev; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageIterable.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageIterable.java deleted file mode 100644 index 82255f6fcb43..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageIterable.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import software.amazon.awssdk.services.dynamodb.document.Page; - - -/** - * @param resource type - * @param low level result type - */ -public class PageIterable implements Iterable> { - private final PageBasedCollection col; - - PageIterable(PageBasedCollection col) { - this.col = col; - } - - @Override - public PageIterator iterator() { - return new PageIterator(col); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageIterator.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageIterator.java deleted file mode 100644 index 90815c52f4ba..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PageIterator.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Iterator; -import software.amazon.awssdk.services.dynamodb.document.Page; - -/** - * @param resource type - * @param low level result type - */ -class PageIterator implements Iterator> { - - private final PageBasedCollection col; - private Page page; - - PageIterator(PageBasedCollection col) { - this.col = col; - } - - @Override - public boolean hasNext() { - Integer max = col.getMaxResultSize(); - if (max != null && max.intValue() <= 0) { - return false; - } - return page == null || page.hasNextPage(); - } - - @Override - public Page next() { - if (page == null) { - page = col.firstPage(); - } else { - page = page.nextPage(); - col.setLastLowLevelResult(page.lowLevelResult()); - } - return page; - } - - @Override - public void remove() { - throw new UnsupportedOperationException("Collection is read-only"); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PutItemImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PutItemImpl.java deleted file mode 100644 index 52c0e2a37905..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/PutItemImpl.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.PutItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.api.PutItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.PutItemSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; - -/** - * The implementation for PutItemApi. - */ -public class PutItemImpl extends AbstractImpl implements PutItemApi { - public PutItemImpl(DynamoDbClient client, Table table) { - super(client, table); - } - - @Override - public PutItemOutcome putItem(Item item) { - return doPutItem(new PutItemSpec().withItem(item)); - } - - @Override - public PutItemOutcome putItem(Item item, Expected... expected) { - return doPutItem(new PutItemSpec() - .withItem(item) - .withExpected(expected)); - } - - @Override - public PutItemOutcome putItem(Item item, String conditionExpression, - Map nameMap, Map valueMap) { - return doPutItem(new PutItemSpec() - .withItem(item) - .withConditionExpression(conditionExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - @Override - public PutItemOutcome putItem(PutItemSpec spec) { - return doPutItem(spec); - } - - private PutItemOutcome doPutItem(PutItemSpec spec) { - // set the table name - String tableName = getTable().getTableName(); - PutItemRequest.Builder requestBuilder = spec.getRequest().toBuilder().tableName(tableName); - // set up the item - Item item = spec.getItem(); - final Map attributes = InternalUtils.toAttributeValues(item); - // set up the expected attribute map, if any - final Map expectedMap = - InternalUtils.toExpectedAttributeValueMap(spec.getExpected()); - // set up the value map, if any (when expression API is used) - final Map attrValMap = - InternalUtils.fromSimpleMap(spec.valueMap()); - // set up the request - requestBuilder.item(attributes) - .expected(expectedMap) - .expressionAttributeNames(spec.nameMap()) - .expressionAttributeValues(attrValMap) - ; - PutItemResponse result = getClient().putItem(requestBuilder.build()); - return new PutItemOutcome(result); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryCollection.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryCollection.java deleted file mode 100644 index 0b00f0479ccb..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryCollection.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.LinkedHashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.Page; -import software.amazon.awssdk.services.dynamodb.document.QueryOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - -class QueryCollection extends ItemCollection { - private final DynamoDbClient client; - private final QuerySpec spec; - private final Map startKey; - - QueryCollection(DynamoDbClient client, QuerySpec spec) { - this.client = client; - this.spec = spec; - Map startKey = - spec.getRequest().exclusiveStartKey(); - this.startKey = startKey == null - ? null - : new LinkedHashMap(startKey); - } - - @Override - public Page firstPage() { - QueryRequest request = spec.getRequest().toBuilder() - .exclusiveStartKey(startKey) - .limit(InternalUtils.minimum( - spec.maxResultSize(), - spec.maxPageSize())) - .build(); - spec.setRequest(request); - QueryResponse result = client.query(request); - QueryOutcome outcome = new QueryOutcome(result); - setLastLowLevelResult(outcome); - return new QueryPage(client, spec, request, 0, outcome); - } - - @Override - public Integer getMaxResultSize() { - return spec.maxResultSize(); - } - - protected void setLastLowLevelResult(QueryOutcome lowLevelResult) { - super.setLastLowLevelResult(lowLevelResult); - QueryResponse result = lowLevelResult.getQueryResponse(); - accumulateStats(result.consumedCapacity(), result.count(), - result.scannedCount()); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryCollectionTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryCollectionTest.java deleted file mode 100644 index 7d7bfff6399d..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryCollectionTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.document.QueryOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; -import software.amazon.awssdk.services.dynamodb.model.Capacity; -import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - -public class QueryCollectionTest { - private static final Random rand = new Random(); - - @Test - public void testEmptyResult() { - QueryCollection col = new QueryCollection(null, new QuerySpec()); - col.setLastLowLevelResult(new QueryOutcome(QueryResponse.builder().build())); - assertTrue(0 == col.getTotalCount()); - assertTrue(0 == col.getTotalScannedCount()); - assertNull(col.getTotalConsumedCapacity()); - } - - @Test - public void setLastLowLevelResult() { - QueryCollection col = new QueryCollection(null, new QuerySpec()); - QueryResponse result = QueryResponse.builder() - .count(rand.nextInt()) - .scannedCount(rand.nextInt()).build(); - - Map gsi = new HashMap(); - gsi.put("gsi1", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - gsi.put("gsi2", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - Map lsi = new HashMap(); - lsi.put("lsi1", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - lsi.put("lsi2", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - ConsumedCapacity consumedCapacity = ConsumedCapacity.builder() - .capacityUnits(rand.nextDouble()) - .table(Capacity.builder().capacityUnits(rand.nextDouble()).build()) - .tableName("tableName") - .globalSecondaryIndexes(gsi) - .localSecondaryIndexes(lsi) - .build(); - // Once - result = result.toBuilder().consumedCapacity(consumedCapacity).build(); - col.setLastLowLevelResult(new QueryOutcome(result)); - - assertTrue(result.count() == col.getTotalCount()); - assertTrue(result.scannedCount() == col.getTotalScannedCount()); - - ConsumedCapacity total = col.getTotalConsumedCapacity(); - assertNotSame(total, consumedCapacity); - assertEquals(total, consumedCapacity); - - assertNotSame(gsi, total.globalSecondaryIndexes()); - assertNotSame(lsi, total.localSecondaryIndexes()); - - // Twice - col.setLastLowLevelResult(new QueryOutcome(result)); - - assertTrue(result.count() * 2 == col.getTotalCount()); - assertTrue(result.scannedCount() * 2 == col.getTotalScannedCount()); - - total = col.getTotalConsumedCapacity(); - assertTrue(total.capacityUnits() == 2 * consumedCapacity.capacityUnits()); - - Map gsiTotal = total.globalSecondaryIndexes(); - Map lsiTotal = total.localSecondaryIndexes(); - assertTrue(2 == gsiTotal.size()); - assertTrue(2 == lsiTotal.size()); - - assertTrue(gsi.get("gsi1").capacityUnits() * 2 == gsiTotal.get("gsi1").capacityUnits()); - assertTrue(gsi.get("gsi2").capacityUnits() * 2 == gsiTotal.get("gsi2").capacityUnits()); - - assertTrue(lsi.get("lsi1").capacityUnits() * 2 == lsiTotal.get("lsi1").capacityUnits()); - assertTrue(lsi.get("lsi2").capacityUnits() * 2 == lsiTotal.get("lsi2").capacityUnits()); - - // A different one - QueryResponse result3 = QueryResponse.builder() - .count(rand.nextInt()) - .scannedCount(rand.nextInt()) - .build(); - - Map gsi3 = new HashMap(); - gsi3.put("gsi3", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - Map lsi3 = new HashMap(); - lsi3.put("lsi3", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - ConsumedCapacity consumedCapacity3 = ConsumedCapacity.builder() - .capacityUnits(rand.nextDouble()) - .table(Capacity.builder().capacityUnits(rand.nextDouble()).build()) - .tableName("tableName") - .globalSecondaryIndexes(gsi3) - .localSecondaryIndexes(lsi3) - .build(); - result3 = result3.toBuilder().consumedCapacity(consumedCapacity3).build(); - col.setLastLowLevelResult(new QueryOutcome(result3)); - - assertTrue(result.count() * 2 + result3.count() == col.getTotalCount()); - assertTrue(result.scannedCount() * 2 + result3.scannedCount() == col.getTotalScannedCount()); - - total = col.getTotalConsumedCapacity(); - assertTrue(total.capacityUnits() == - 2 * consumedCapacity.capacityUnits() - + consumedCapacity3.capacityUnits()); - - gsiTotal = total.globalSecondaryIndexes(); - lsiTotal = total.localSecondaryIndexes(); - assertTrue(3 == gsiTotal.size()); - assertTrue(3 == lsiTotal.size()); - - assertTrue(gsi.get("gsi1").capacityUnits() * 2 == gsiTotal.get("gsi1").capacityUnits()); - assertTrue(gsi.get("gsi2").capacityUnits() * 2 == gsiTotal.get("gsi2").capacityUnits()); - assertTrue(gsi3.get("gsi3").capacityUnits() == gsiTotal.get("gsi3").capacityUnits()); - - assertTrue(lsi.get("lsi1").capacityUnits() * 2 == lsiTotal.get("lsi1").capacityUnits()); - assertTrue(lsi.get("lsi2").capacityUnits() * 2 == lsiTotal.get("lsi2").capacityUnits()); - assertTrue(lsi3.get("lsi3").capacityUnits() == lsiTotal.get("lsi3").capacityUnits()); - - // An empty one - QueryResponse result4 = QueryResponse.builder().build(); - ConsumedCapacity consumedCapacity4 = ConsumedCapacity.builder().build(); - result4 = result4.toBuilder().consumedCapacity(consumedCapacity4).build(); - col.setLastLowLevelResult(new QueryOutcome(result4)); - - // all assertions are expected to be the same as the last set of assertions - assertTrue(result.count() * 2 + result3.count() == col.getTotalCount()); - assertTrue(result.scannedCount() * 2 + result3.scannedCount() == col.getTotalScannedCount()); - - total = col.getTotalConsumedCapacity(); - assertTrue(total.capacityUnits() == - 2 * consumedCapacity.capacityUnits() - + consumedCapacity3.capacityUnits()); - - gsiTotal = total.globalSecondaryIndexes(); - lsiTotal = total.localSecondaryIndexes(); - assertTrue(3 == gsiTotal.size()); - assertTrue(3 == lsiTotal.size()); - - assertTrue(gsi.get("gsi1").capacityUnits() * 2 == gsiTotal.get("gsi1").capacityUnits()); - assertTrue(gsi.get("gsi2").capacityUnits() * 2 == gsiTotal.get("gsi2").capacityUnits()); - assertTrue(gsi3.get("gsi3").capacityUnits() == gsiTotal.get("gsi3").capacityUnits()); - - assertTrue(lsi.get("lsi1").capacityUnits() * 2 == lsiTotal.get("lsi1").capacityUnits()); - assertTrue(lsi.get("lsi2").capacityUnits() * 2 == lsiTotal.get("lsi2").capacityUnits()); - assertTrue(lsi3.get("lsi3").capacityUnits() == lsiTotal.get("lsi3").capacityUnits()); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryImpl.java deleted file mode 100644 index 764b0fc27f32..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryImpl.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.KeyCondition; -import software.amazon.awssdk.services.dynamodb.document.QueryFilter; -import software.amazon.awssdk.services.dynamodb.document.QueryOutcome; -import software.amazon.awssdk.services.dynamodb.document.RangeKeyCondition; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.api.QueryApi; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; - -/** - * The implementation for QueryApi of a table. - */ -public class QueryImpl extends AbstractImpl implements QueryApi { - public QueryImpl(DynamoDbClient client, Table table) { - super(client, table); - } - - @Override - public ItemCollection query(String hashKeyName, Object hashKey) { - return doQuery(new QuerySpec() - .withHashKey(new KeyAttribute(hashKeyName, hashKey))); - } - - @Override - public ItemCollection query(KeyAttribute hashKey) { - return doQuery(new QuerySpec().withHashKey(hashKey)); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition) { - return doQuery(new QuerySpec().withHashKey(hashKey) - .withRangeKeyCondition(rangeKeyCondition)); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, QueryFilter... queryFilters) { - return doQuery(new QuerySpec().withHashKey(hashKey) - .withRangeKeyCondition(rangeKeyCondition) - .withQueryFilters(queryFilters)); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, String filterExpression, - Map nameMap, Map valueMap) { - return doQuery(new QuerySpec().withHashKey(hashKey) - .withRangeKeyCondition(rangeKeyCondition) - .withFilterExpression(filterExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - @Override - public ItemCollection query(KeyAttribute hashKey, - RangeKeyCondition rangeKeyCondition, String filterExpression, - String projectionExpression, Map nameMap, - Map valueMap) { - return doQuery(new QuerySpec().withHashKey(hashKey) - .withRangeKeyCondition(rangeKeyCondition) - .withFilterExpression(filterExpression) - .withProjectionExpression(projectionExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - @Override - public ItemCollection query(QuerySpec spec) { - return doQuery(spec); - } - - protected ItemCollection doQuery(QuerySpec spec) { - // set the table name - String tableName = getTable().getTableName(); - QueryRequest.Builder requestBuilder = spec.getRequest().toBuilder().tableName(tableName); - - Map conditions = new LinkedHashMap<>(); - - if (spec.getRequest().keyConditions() != null) { - conditions.putAll(spec.getRequest().keyConditions()); - } - - // hash key - final KeyAttribute hashKey = spec.getHashKey(); - if (hashKey != null) { - conditions.put(hashKey.name(), - Condition.builder() - .comparisonOperator(ComparisonOperator.EQ) - .attributeValueList(InternalUtils.toAttributeValue(hashKey.value())).build()); - } - // range key condition - RangeKeyCondition rangeKeyCond = spec.getRangeKeyCondition(); - if (rangeKeyCond != null) { - KeyCondition keyCond = rangeKeyCond.getKeyCondition(); - if (keyCond == null) { - throw new IllegalArgumentException("key condition not specified in range key condition"); - } - Object[] values = rangeKeyCond.values(); - if (values == null) { - throw new IllegalArgumentException("key condition values not specified in range key condition"); - } - conditions.put(rangeKeyCond.getAttrName(), - Condition.builder() - .comparisonOperator(keyCond.toComparisonOperator()) - .attributeValueList(InternalUtils.toAttributeValues(values)).build()); - } - - requestBuilder.keyConditions(conditions); - - // query filters; - Collection filters = spec.getQueryFilters(); - if (filters != null) { - requestBuilder.queryFilter(InternalUtils.toAttributeConditionMap(filters)); - } - - // set up the start key, if any - Collection startKey = spec.getExclusiveStartKey(); - if (startKey != null) { - requestBuilder.exclusiveStartKey(InternalUtils.toAttributeValueMap(startKey)); - } - - // set up the value map, if any (when expression API is used) - final Map attrValMap = InternalUtils.fromSimpleMap(spec.valueMap()); - // set up expressions, if any - requestBuilder.expressionAttributeNames(spec.nameMap()) - .expressionAttributeValues(attrValMap); - - spec.setRequest(requestBuilder.build()); - return new QueryCollection(getClient(), spec); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition) { - return query(new KeyAttribute(hashKeyName, hashKeyValue), rangeKeyCondition); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - QueryFilter... queryFilters) { - return query(new KeyAttribute(hashKeyName, hashKeyValue), - rangeKeyCondition, queryFilters); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - String filterExpression, Map nameMap, - Map valueMap) { - return query(new KeyAttribute(hashKeyName, hashKeyValue), - rangeKeyCondition, filterExpression, nameMap, valueMap); - } - - @Override - public ItemCollection query(String hashKeyName, - Object hashKeyValue, RangeKeyCondition rangeKeyCondition, - String filterExpression, String projectionExpression, - Map nameMap, Map valueMap) { - return query(new KeyAttribute(hashKeyName, hashKeyValue), - rangeKeyCondition, filterExpression, projectionExpression, - nameMap, valueMap); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryPage.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryPage.java deleted file mode 100644 index d27e56d89587..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/QueryPage.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toItemList; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.Page; -import software.amazon.awssdk.services.dynamodb.document.QueryOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.QuerySpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - -class QueryPage extends Page { - private final DynamoDbClient client; - private final QuerySpec spec; - private QueryRequest request; - private final int index; - private final Map lastEvaluatedKey; - - QueryPage( - DynamoDbClient client, - QuerySpec spec, - QueryRequest request, - int index, - QueryOutcome outcome) { - super(Collections.unmodifiableList( - toItemList(outcome.getQueryResponse().items())), - outcome); - this.client = client; - this.spec = spec; - this.request = request; - this.index = index; - - final Integer max = spec.maxResultSize(); - final QueryResponse result = outcome.getQueryResponse(); - final List ilist = result.items(); - final int size = ilist == null ? 0 : ilist.size(); - if (max != null && (index + size) > max) { - this.lastEvaluatedKey = null; - } else { - this.lastEvaluatedKey = result.lastEvaluatedKey(); - } - } - - @Override - public boolean hasNextPage() { - if (lastEvaluatedKey == null) { - return false; - } - Integer max = spec.maxResultSize(); - if (max == null) { - return true; - } - return nextRequestLimit(max.intValue()) > 0; - } - - private int nextRequestLimit(int max) { - int nextIndex = index + this.size(); - return InternalUtils.minimum( - max - nextIndex, - spec.maxPageSize()); - } - - @Override - public Page nextPage() { - if (lastEvaluatedKey == null) { - throw new NoSuchElementException("No more pages"); - } - final Integer max = spec.maxResultSize(); - if (max != null) { - int nextLimit = nextRequestLimit(max.intValue()); - if (nextLimit == 0) { - throw new NoSuchElementException("No more pages"); - } - request = request.toBuilder().limit(nextLimit).build(); - } - request = request.toBuilder().exclusiveStartKey(lastEvaluatedKey).build(); - QueryResponse result = client.query(request); - final int nextIndex = index + this.size(); - return new QueryPage(client, spec, request, nextIndex, - new QueryOutcome(result)); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanCollection.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanCollection.java deleted file mode 100644 index 3eba543f89e8..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanCollection.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.LinkedHashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.Page; -import software.amazon.awssdk.services.dynamodb.document.ScanOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -class ScanCollection extends ItemCollection { - - private final DynamoDbClient client; - private final ScanSpec spec; - private final Map startKey; - - ScanCollection(DynamoDbClient client, ScanSpec spec) { - this.client = client; - this.spec = spec; - Map startKey = spec.getRequest() - .exclusiveStartKey(); - this.startKey = startKey == null ? null : new LinkedHashMap(startKey); - } - - @Override - public Page firstPage() { - ScanRequest request = spec.getRequest(); - request = request.toBuilder() - .exclusiveStartKey(startKey) - .limit(InternalUtils.minimum( - spec.maxResultSize(), - spec.maxPageSize())) - .build(); - - spec.setRequest(request); - - ScanResponse result = client.scan(request); - ScanOutcome outcome = new ScanOutcome(result); - setLastLowLevelResult(outcome); - return new ScanPage(client, spec, request, 0, outcome); - } - - @Override - public Integer getMaxResultSize() { - return spec.maxResultSize(); - } - - protected void setLastLowLevelResult(ScanOutcome lowLevelResult) { - super.setLastLowLevelResult(lowLevelResult); - ScanResponse result = lowLevelResult.scanResult(); - accumulateStats(result.consumedCapacity(), result.count(), - result.scannedCount()); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanCollectionTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanCollectionTest.java deleted file mode 100644 index 6c0e87fad17e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanCollectionTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.document.ScanOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; -import software.amazon.awssdk.services.dynamodb.model.Capacity; -import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -public class ScanCollectionTest { - private static final Random rand = new Random(); - - @Test - public void testEmptyResult() { - ScanCollection col = new ScanCollection(null, new ScanSpec()); - col.setLastLowLevelResult(new ScanOutcome(ScanResponse.builder().build())); - assertTrue(0 == col.getTotalCount()); - assertTrue(0 == col.getTotalScannedCount()); - assertNull(col.getTotalConsumedCapacity()); - } - - @Test - public void setLastLowLevelResult() { - ScanCollection col = new ScanCollection(null, new ScanSpec()); - ScanResponse result = ScanResponse.builder() - .count(rand.nextInt()) - .scannedCount(rand.nextInt()).build(); - - Map gsi = new HashMap(); - gsi.put("gsi1", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - gsi.put("gsi2", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - Map lsi = new HashMap(); - lsi.put("lsi1", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - lsi.put("lsi2", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - ConsumedCapacity consumedCapacity = ConsumedCapacity.builder() - .capacityUnits(rand.nextDouble()) - .table(Capacity.builder().capacityUnits(rand.nextDouble()).build()) - .tableName("tableName") - .globalSecondaryIndexes(gsi) - .localSecondaryIndexes(lsi).build(); - // Once - result = result.toBuilder().consumedCapacity(consumedCapacity).build(); - col.setLastLowLevelResult(new ScanOutcome(result)); - - assertTrue(result.count() == col.getTotalCount()); - assertTrue(result.scannedCount() == col.getTotalScannedCount()); - - ConsumedCapacity total = col.getTotalConsumedCapacity(); - assertNotSame(total, consumedCapacity); - assertEquals(total, consumedCapacity); - - assertNotSame(gsi, total.globalSecondaryIndexes()); - assertNotSame(lsi, total.localSecondaryIndexes()); - - // Twice - col.setLastLowLevelResult(new ScanOutcome(result)); - - assertTrue(result.count() * 2 == col.getTotalCount()); - assertTrue(result.scannedCount() * 2 == col.getTotalScannedCount()); - - total = col.getTotalConsumedCapacity(); - assertTrue(total.capacityUnits() == 2 * consumedCapacity.capacityUnits()); - - Map gsiTotal = total.globalSecondaryIndexes(); - Map lsiTotal = total.localSecondaryIndexes(); - assertTrue(2 == gsiTotal.size()); - assertTrue(2 == lsiTotal.size()); - - assertTrue(gsi.get("gsi1").capacityUnits() * 2 == gsiTotal.get("gsi1").capacityUnits()); - assertTrue(gsi.get("gsi2").capacityUnits() * 2 == gsiTotal.get("gsi2").capacityUnits()); - - assertTrue(lsi.get("lsi1").capacityUnits() * 2 == lsiTotal.get("lsi1").capacityUnits()); - assertTrue(lsi.get("lsi2").capacityUnits() * 2 == lsiTotal.get("lsi2").capacityUnits()); - - // A different one - ScanResponse result3 = ScanResponse.builder() - .count(rand.nextInt()) - .scannedCount(rand.nextInt()) - .build(); - - Map gsi3 = new HashMap(); - gsi3.put("gsi3", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - Map lsi3 = new HashMap(); - lsi3.put("lsi3", Capacity.builder().capacityUnits(rand.nextDouble()).build()); - - ConsumedCapacity consumedCapacity3 = ConsumedCapacity.builder() - .capacityUnits(rand.nextDouble()) - .table(Capacity.builder().capacityUnits(rand.nextDouble()).build()) - .tableName("tableName") - .globalSecondaryIndexes(gsi3) - .localSecondaryIndexes(lsi3) - .build(); - result3 = result3.toBuilder().consumedCapacity(consumedCapacity3).build(); - col.setLastLowLevelResult(new ScanOutcome(result3)); - - assertTrue(result.count() * 2 + result3.count() == col.getTotalCount()); - assertTrue(result.scannedCount() * 2 + result3.scannedCount() == col.getTotalScannedCount()); - - total = col.getTotalConsumedCapacity(); - assertTrue(total.capacityUnits() == - 2 * consumedCapacity.capacityUnits() - + consumedCapacity3.capacityUnits()); - - gsiTotal = total.globalSecondaryIndexes(); - lsiTotal = total.localSecondaryIndexes(); - assertTrue(3 == gsiTotal.size()); - assertTrue(3 == lsiTotal.size()); - - assertTrue(gsi.get("gsi1").capacityUnits() * 2 == gsiTotal.get("gsi1").capacityUnits()); - assertTrue(gsi.get("gsi2").capacityUnits() * 2 == gsiTotal.get("gsi2").capacityUnits()); - assertTrue(gsi3.get("gsi3").capacityUnits() == gsiTotal.get("gsi3").capacityUnits()); - - assertTrue(lsi.get("lsi1").capacityUnits() * 2 == lsiTotal.get("lsi1").capacityUnits()); - assertTrue(lsi.get("lsi2").capacityUnits() * 2 == lsiTotal.get("lsi2").capacityUnits()); - assertTrue(lsi3.get("lsi3").capacityUnits() == lsiTotal.get("lsi3").capacityUnits()); - - // An empty one - ScanResponse result4 = ScanResponse.builder().build(); - ConsumedCapacity consumedCapacity4 = ConsumedCapacity.builder().build(); - result4 = result4.toBuilder().consumedCapacity(consumedCapacity4).build(); - col.setLastLowLevelResult(new ScanOutcome(result4)); - - // all assertions are expected to be the same as the last set of assertions - assertTrue(result.count() * 2 + result3.count() == col.getTotalCount()); - assertTrue(result.scannedCount() * 2 + result3.scannedCount() == col.getTotalScannedCount()); - - total = col.getTotalConsumedCapacity(); - assertTrue(total.capacityUnits() == - 2 * consumedCapacity.capacityUnits() - + consumedCapacity3.capacityUnits()); - - gsiTotal = total.globalSecondaryIndexes(); - lsiTotal = total.localSecondaryIndexes(); - assertTrue(3 == gsiTotal.size()); - assertTrue(3 == lsiTotal.size()); - - assertTrue(gsi.get("gsi1").capacityUnits() * 2 == gsiTotal.get("gsi1").capacityUnits()); - assertTrue(gsi.get("gsi2").capacityUnits() * 2 == gsiTotal.get("gsi2").capacityUnits()); - assertTrue(gsi3.get("gsi3").capacityUnits() == gsiTotal.get("gsi3").capacityUnits()); - - assertTrue(lsi.get("lsi1").capacityUnits() * 2 == lsiTotal.get("lsi1").capacityUnits()); - assertTrue(lsi.get("lsi2").capacityUnits() * 2 == lsiTotal.get("lsi2").capacityUnits()); - assertTrue(lsi3.get("lsi3").capacityUnits() == lsiTotal.get("lsi3").capacityUnits()); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanImpl.java deleted file mode 100644 index 9a84ae4c83c3..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanImpl.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Collection; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.ItemCollection; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.ScanFilter; -import software.amazon.awssdk.services.dynamodb.document.ScanOutcome; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.api.ScanApi; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; - -/** - * The implementation for ScanApi. - */ -public class ScanImpl extends AbstractImpl implements ScanApi { - public ScanImpl(DynamoDbClient client, Table table) { - super(client, table); - } - - @Override - public ItemCollection scan(ScanFilter... scanFilters) { - return doScan(new ScanSpec() - .withScanFilters(scanFilters)); - } - - - @Override - public ItemCollection scan(String filterExpression, - Map nameMap, Map valueMap) { - return doScan(new ScanSpec() - .withFilterExpression(filterExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - - @Override - public ItemCollection scan(String filterExpression, - String projectionExpression, Map nameMap, - Map valueMap) { - return doScan(new ScanSpec() - .withFilterExpression(filterExpression) - .withProjectionExpression(projectionExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - @Override - public ItemCollection scan(ScanSpec spec) { - return doScan(spec); - } - - protected ItemCollection doScan(ScanSpec spec) { - // set the table name - String tableName = getTable().getTableName(); - ScanRequest.Builder requestBuilder = spec.getRequest().toBuilder().tableName(tableName); - - // set up the start key, if any - Collection startKey = spec.getExclusiveStartKey(); - if (startKey != null) { - requestBuilder.exclusiveStartKey(InternalUtils.toAttributeValueMap(startKey)); - } - - // scan filters; - Collection filters = spec.scanFilters(); - if (filters != null) { - requestBuilder.scanFilter(InternalUtils.toAttributeConditionMap(filters)); - } - - // set up the value map, if any (when expression API is used) - final Map attrValMap = InternalUtils.fromSimpleMap(spec.valueMap()); - // set up expressions, if any - requestBuilder.expressionAttributeNames(spec.nameMap()) - .expressionAttributeValues(attrValMap); - - spec.setRequest(requestBuilder.build()); - return new ScanCollection(getClient(), spec); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanPage.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanPage.java deleted file mode 100644 index 2935f80a6713..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ScanPage.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import static software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils.toItemList; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.document.Page; -import software.amazon.awssdk.services.dynamodb.document.ScanOutcome; -import software.amazon.awssdk.services.dynamodb.document.spec.ScanSpec; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -class ScanPage extends Page { - - private final DynamoDbClient client; - private final ScanSpec spec; - private ScanRequest request; - private final int index; - private final Map lastEvaluatedKey; - - ScanPage( - DynamoDbClient client, - ScanSpec spec, - ScanRequest request, - int index, - ScanOutcome outcome) { - super(Collections.unmodifiableList( - toItemList(outcome.scanResult().items())), - outcome); - this.client = client; - this.spec = spec; - this.request = request; - this.index = index; - - final Integer max = spec.maxResultSize(); - final ScanResponse result = outcome.scanResult(); - final List ilist = result.items(); - final int size = ilist == null ? 0 : ilist.size(); - if (max != null && (index + size) > max) { - this.lastEvaluatedKey = null; - } else { - this.lastEvaluatedKey = result.lastEvaluatedKey(); - } - } - - @Override - public boolean hasNextPage() { - if (lastEvaluatedKey == null) { - return false; - } - Integer max = spec.maxResultSize(); - if (max == null) { - return true; - } - return nextRequestLimit(max.intValue()) > 0; - } - - private int nextRequestLimit(int max) { - int nextIndex = index + this.size(); - return InternalUtils.minimum( - max - nextIndex, - spec.maxPageSize()); - } - - @Override - public Page nextPage() { - if (lastEvaluatedKey == null) { - throw new NoSuchElementException("No more pages"); - } - final Integer max = spec.maxResultSize(); - if (max != null) { - int nextLimit = nextRequestLimit(max.intValue()); - if (nextLimit == 0) { - throw new NoSuchElementException("No more pages"); - } - request = request.toBuilder().limit(nextLimit).build(); - } - request = request.toBuilder().exclusiveStartKey(lastEvaluatedKey).build(); - // fire off request to the server side - ScanResponse result = client.scan(request); - final int nextIndex = index + this.size(); - return new ScanPage(client, spec, request, nextIndex, - new ScanOutcome(result)); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/UpdateItemImpl.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/UpdateItemImpl.java deleted file mode 100644 index c6ecad296a81..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/UpdateItemImpl.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -import java.util.Collection; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.document.AttributeUpdate; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.Table; -import software.amazon.awssdk.services.dynamodb.document.UpdateItemOutcome; -import software.amazon.awssdk.services.dynamodb.document.api.UpdateItemApi; -import software.amazon.awssdk.services.dynamodb.document.spec.UpdateItemSpec; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; - -/** - * The implementation for UpdateItemApi. - */ -public class UpdateItemImpl implements UpdateItemApi { - - private final Table table; - private final DynamoDbClient client; - - public UpdateItemImpl(DynamoDbClient client, Table table) { - this.client = client; - this.table = table; - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - AttributeUpdate... attributeUpdates) { - return updateItem(new UpdateItemSpec() - .withPrimaryKey(primaryKey) - .withAttributeUpdate(attributeUpdates)); - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - Collection expected, AttributeUpdate... attributeUpdates) { - return updateItem(new UpdateItemSpec() - .withPrimaryKey(primaryKey) - .withExpected(expected) - .withAttributeUpdate(attributeUpdates)); - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - String updateExpression, Map nameMap, - Map valueMap) { - return updateItem(new UpdateItemSpec() - .withPrimaryKey(primaryKey) - .withUpdateExpression(updateExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - @Override - public UpdateItemOutcome updateItem(PrimaryKey primaryKey, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap) { - - return updateItem(new UpdateItemSpec().withPrimaryKey(primaryKey) - .withUpdateExpression(updateExpression) - .withConditionExpression(conditionExpression) - .withNameMap(nameMap) - .valueMap(valueMap)); - } - - @Override - public UpdateItemOutcome updateItem(UpdateItemSpec spec) { - return doUpdateItem(spec); - } - - private UpdateItemOutcome doUpdateItem(UpdateItemSpec spec) { - final UpdateItemRequest.Builder requestBuilder = spec.getRequest().toBuilder(); - requestBuilder.key(InternalUtils.toAttributeValueMap(spec.getKeyComponents())); - requestBuilder.tableName(table.getTableName()); - final Collection expected = spec.getExpected(); - final Map expectedMap = - InternalUtils.toExpectedAttributeValueMap(expected); - requestBuilder.expected(expectedMap); - requestBuilder.attributeUpdates( - InternalUtils.toAttributeValueUpdate(spec.getAttributeUpdate())); - requestBuilder.expressionAttributeNames(spec.nameMap()); - requestBuilder.expressionAttributeValues( - InternalUtils.fromSimpleMap(spec.valueMap())); - return new UpdateItemOutcome(client.updateItem(requestBuilder.build())); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, AttributeUpdate... attributeUpdates) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue), - attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, String rangeKeyName, Object rangeKeyValue, - AttributeUpdate... attributeUpdates) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue), attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, Collection expected, - AttributeUpdate... attributeUpdates) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue), - expected, - attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem( - String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - Collection expected, - AttributeUpdate... attributeUpdates) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue), - expected, - attributeUpdates); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, String updateExpression, - Map nameMap, Map valueMap) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue), - updateExpression, nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String updateExpression, Map nameMap, - Map valueMap) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue), - updateExpression, nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, - Object hashKeyValue, String updateExpression, - String conditionExpression, Map nameMap, - Map valueMap) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue), - updateExpression, conditionExpression, nameMap, valueMap); - } - - @Override - public UpdateItemOutcome updateItem(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue, - String updateExpression, String conditionExpression, - Map nameMap, Map valueMap) { - return updateItem(new PrimaryKey(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue), - updateExpression, conditionExpression, nameMap, valueMap); - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ValueTransformer.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ValueTransformer.java deleted file mode 100644 index 3cd5f1502a8e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/internal/ValueTransformer.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.internal; - -/** - * Internal value transformer SPI. - */ -abstract class ValueTransformer { - abstract Object transform(Object value); -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractCollectionSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractCollectionSpec.java deleted file mode 100644 index fabb06929542..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractCollectionSpec.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - - -import software.amazon.awssdk.awscore.AwsRequest; - -/** - * Abstract implementation class for parameter specification that involves - * collection of results. - * - * @param request type - */ -abstract class AbstractCollectionSpec - extends AbstractSpec { - - private Integer maxPageSize; - /** - * The maximum number of resources to be retrieved; or null if there is no - * limit. - */ - private Integer maxResultSize; - - - AbstractCollectionSpec(T req) { - super(req); - } - - public AbstractCollectionSpec withMaxResultSize( - Integer maxResultSize) { - this.maxResultSize = maxResultSize; - return this; - } - - public AbstractCollectionSpec withMaxResultSize( - int maxResultSize) { - this.maxResultSize = maxResultSize; - return this; - } - - public AbstractCollectionSpec withMaxPageSize(Integer maxPageSize) { - this.maxPageSize = maxPageSize; - return this; - } - - public AbstractCollectionSpec withMaxPageSize(int maxPageSize) { - this.maxPageSize = maxPageSize; - return this; - } - - /** - * The maximum number of resources to be retrieved in this query, including - * all the resources in all pages to be retrieved. - */ - public Integer maxResultSize() { - return maxResultSize; - } - - public void setMaxResultSize(Integer maxResultSize) { - this.maxResultSize = maxResultSize; - } - - public void setMaxResultSize(int maxResultSize) { - this.maxResultSize = maxResultSize; - } - - /** - * The maximum number of resources to be retrieved in a single page; used - * for pagination purposes. - */ - public Integer maxPageSize() { - return maxPageSize; - } - - public void setMaxPageSize(Integer value) { - maxPageSize = value; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractSpec.java deleted file mode 100644 index ffbd202afa6c..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractSpec.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import software.amazon.awssdk.awscore.AwsRequest; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - -/** - * Abstract implementation base class for parameter specification. - * - * @param request type - */ -class AbstractSpec { - private T req; - - AbstractSpec(T req) { - setRequest(req); - } - - public void setRequest(T req) { - InternalUtils.applyUserAgent(req); - this.req = req; - } - - /** - * Internal method. Not meant to be called directly. May change without notice. - */ - public T getRequest() { - return req; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractSpecWithPrimaryKey.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractSpecWithPrimaryKey.java deleted file mode 100644 index 4e46f55727f8..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/AbstractSpecWithPrimaryKey.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import software.amazon.awssdk.awscore.AwsRequest; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; - -/** - * Common base class for parameter specification that involves a primary key. - */ -public class AbstractSpecWithPrimaryKey - extends AbstractSpec { - private Collection keyComponents; - - protected AbstractSpecWithPrimaryKey(T request) { - super(request); - } - - /** - * Returns the primary key components that has been specified. - */ - public final Collection getKeyComponents() { - return keyComponents; - } - - /** - * Sets the primary key with the specified key components. - */ - public AbstractSpecWithPrimaryKey withPrimaryKey(KeyAttribute... components) { - if (components == null) { - this.keyComponents = null; - } else { - this.keyComponents = Arrays.asList(components); - } - return this; - } - - /** - * Sets the primary key. - */ - public AbstractSpecWithPrimaryKey withPrimaryKey(PrimaryKey primaryKey) { - if (primaryKey == null) { - this.keyComponents = null; - } else { - this.keyComponents = primaryKey.getComponents(); - } - return this; - } - - /** - * Sets the primary key with the specified hash-only key name and value. - */ - public AbstractSpecWithPrimaryKey withPrimaryKey(String hashKeyName, Object hashKeyValue) { - if (hashKeyName == null) { - throw new IllegalArgumentException(); - } - withPrimaryKey(new PrimaryKey(hashKeyName, hashKeyValue)); - return this; - } - - /** - * Sets the primary key with the specified hash key and range key. - */ - public AbstractSpecWithPrimaryKey withPrimaryKey(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - if (hashKeyName == null) { - throw new IllegalArgumentException("Invalid hash key name"); - } - if (rangeKeyName == null) { - throw new IllegalArgumentException("Invalid range key name"); - } - if (hashKeyName.equals(rangeKeyName)) { - throw new IllegalArgumentException("Names of hash and range keys must not be the same"); - } - withPrimaryKey(new PrimaryKey(hashKeyName, hashKeyValue, - rangeKeyName, rangeKeyValue)); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/BatchGetItemSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/BatchGetItemSpec.java deleted file mode 100644 index 998df0aba0ac..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/BatchGetItemSpec.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.TableKeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; - -/** - * Full parameter specification for the BatchGetItem API. - */ -public class BatchGetItemSpec extends AbstractSpec { - private Collection tableKeyAndAttributes; - private Map unprocessedKeys; - - public BatchGetItemSpec() { - super(BatchGetItemRequest.builder().build()); - } - - public Collection getTableKeysAndAttributes() { - return tableKeyAndAttributes; - } - - public BatchGetItemSpec withTableKeyAndAttributes( - TableKeysAndAttributes... tableKeyAndAttributes) { - if (tableKeyAndAttributes == null) { - this.tableKeyAndAttributes = null; - } else { - Set names = new LinkedHashSet(); - for (TableKeysAndAttributes e : tableKeyAndAttributes) { - names.add(e.getTableName()); - } - if (names.size() != tableKeyAndAttributes.length) { - throw new IllegalArgumentException( - "table names must not duplicate in the list of TableKeysAndAttributes"); - } - this.tableKeyAndAttributes = Arrays.asList(tableKeyAndAttributes); - } - return this; - } - - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - - public BatchGetItemSpec withReturnConsumedCapacity(ReturnConsumedCapacity capacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(capacity).build()); - return this; - } - - public Map getUnprocessedKeys() { - return unprocessedKeys; - } - - public BatchGetItemSpec withUnprocessedKeys( - Map unprocessedKeys) { - this.unprocessedKeys = Collections.unmodifiableMap( - new LinkedHashMap(unprocessedKeys)); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/BatchWriteItemSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/BatchWriteItemSpec.java deleted file mode 100644 index 5123f30b92ec..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/BatchWriteItemSpec.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.TableWriteItems; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * Full parameter specification for the BatchWriteItem API. - */ -public class BatchWriteItemSpec extends AbstractSpec { - private Collection tableWriteItems; - private Map> unprocessedItems; - - public BatchWriteItemSpec() { - super(BatchWriteItemRequest.builder().build()); - } - - public Collection getTableWriteItems() { - return tableWriteItems; - } - - public BatchWriteItemSpec withTableWriteItems( - TableWriteItems... tableWriteItems) { - if (tableWriteItems == null) { - this.tableWriteItems = null; - } else { - Set names = new LinkedHashSet(); - for (TableWriteItems e : tableWriteItems) { - names.add(e.getTableName()); - } - if (names.size() != tableWriteItems.length) { - throw new IllegalArgumentException( - "table names must not duplicate in the list of TableWriteItems"); - } - this.tableWriteItems = Arrays.asList(tableWriteItems); - } - return this; - } - - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - - public BatchWriteItemSpec withReturnConsumedCapacity(ReturnConsumedCapacity capacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(capacity).build()); - return this; - } - - public Map> getUnprocessedItems() { - return unprocessedItems; - } - - public BatchWriteItemSpec withUnprocessedItems( - Map> unprocessedItems) { - this.unprocessedItems = Collections.unmodifiableMap( - new LinkedHashMap>(unprocessedItems)); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/DeleteItemSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/DeleteItemSpec.java deleted file mode 100644 index 4c450da62340..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/DeleteItemSpec.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.ReturnItemCollectionMetrics; -import software.amazon.awssdk.services.dynamodb.model.ReturnValue; - -/** - * Full parameter specification for the DeleteItem API. - */ -public class DeleteItemSpec extends AbstractSpecWithPrimaryKey { - private Collection expected; - - private Map nameMap; - private Map valueMap; - - public DeleteItemSpec() { - super(DeleteItemRequest.builder().build()); - } - - @Override - public DeleteItemSpec withPrimaryKey(KeyAttribute... components) { - super.withPrimaryKey(components); - return this; - } - - @Override - public DeleteItemSpec withPrimaryKey(PrimaryKey primaryKey) { - super.withPrimaryKey(primaryKey); - return this; - } - - @Override - public DeleteItemSpec withPrimaryKey(String hashKeyName, Object hashKeyValue) { - super.withPrimaryKey(hashKeyName, hashKeyValue); - return this; - } - - @Override - public DeleteItemSpec withPrimaryKey(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - super.withPrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue); - return this; - } - - public Collection getExpected() { - return expected; - } - - public DeleteItemSpec withExpected(Expected... expected) { - if (expected == null) { - this.expected = null; - return this; - } - return withExpected(Arrays.asList(expected)); - } - - public DeleteItemSpec withExpected(Collection expected) { - if (expected == null) { - this.expected = null; - return this; - } - Set names = new LinkedHashSet(); - for (Expected e : expected) { - names.add(e.getAttribute()); - } - if (names.size() != expected.size()) { - throw new IllegalArgumentException( - "attribute names must not duplicate in the list of expected"); - } - this.expected = Collections.unmodifiableCollection(expected); - return this; - } - - public String getConditionExpression() { - return getRequest().conditionExpression(); - } - - public DeleteItemSpec withConditionExpression(String conditionExpression) { - setRequest(getRequest().toBuilder().conditionExpression(conditionExpression).build()); - return this; - } - - public Map nameMap() { - return nameMap; - } - - /** - * Applicable only when an expression has been specified. - * Used to specify the actual values for the attribute-name placeholders, - * where the value in the map can either be string for simple attribute - * name, or a JSON path expression. - */ - public DeleteItemSpec withNameMap(Map nameMap) { - if (nameMap == null) { - this.nameMap = null; - } else { - this.nameMap = Collections.unmodifiableMap( - new LinkedHashMap(nameMap)); - } - return this; - } - - public Map valueMap() { - return valueMap; - } - - /** - * Applicable only when an expression has been specified. Used to - * specify the actual values for the attribute-value placeholders. - */ - public DeleteItemSpec valueMap(Map valueMap) { - if (valueMap == null) { - this.valueMap = null; - } else { - this.valueMap = Collections.unmodifiableMap( - new LinkedHashMap(valueMap)); - } - return this; - } - - public String getConditionalOperator() { - return getRequest().conditionalOperatorAsString(); - } - - public DeleteItemSpec withConditionalOperator(ConditionalOperator conditionalOperator) { - setRequest(getRequest().toBuilder().conditionalOperator(conditionalOperator).build()); - return this; - } - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - public DeleteItemSpec withReturnConsumedCapacity( - ReturnConsumedCapacity returnConsumedCapacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(returnConsumedCapacity).build()); - return this; - } - - public String getReturnItemCollectionMetrics() { - return getRequest().returnItemCollectionMetricsAsString(); - } - - public DeleteItemSpec withReturnItemCollectionMetrics( - ReturnItemCollectionMetrics returnItemCollectionMetrics) { - setRequest(getRequest().toBuilder().returnItemCollectionMetrics(returnItemCollectionMetrics).build()); - return this; - } - - public String getReturnValues() { - return getRequest().returnValuesAsString(); - } - - public DeleteItemSpec withReturnValues(ReturnValue returnValues) { - setRequest(getRequest().toBuilder().returnValues(returnValues).build()); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/GetItemSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/GetItemSpec.java deleted file mode 100644 index 14d621f424b0..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/GetItemSpec.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; - -/** - * Full parameter specification for the GetItem API. - */ -public class GetItemSpec extends AbstractSpecWithPrimaryKey { - private Map nameMap; - - public GetItemSpec() { - super(GetItemRequest.builder().build()); - } - - @Override - public GetItemSpec withPrimaryKey(KeyAttribute... components) { - super.withPrimaryKey(components); - return this; - } - - @Override - public GetItemSpec withPrimaryKey(PrimaryKey primaryKey) { - super.withPrimaryKey(primaryKey); - return this; - } - - @Override - public GetItemSpec withPrimaryKey(String hashKeyName, Object hashKeyValue) { - super.withPrimaryKey(hashKeyName, hashKeyValue); - return this; - } - - @Override - public GetItemSpec withPrimaryKey(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - super.withPrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue); - return this; - } - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - public GetItemSpec withReturnConsumedCapacity(ReturnConsumedCapacity capacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(capacity).build()); - return this; - } - - public List getAttributesToGet() { - return getRequest().attributesToGet(); - } - - public GetItemSpec withAttributesToGet(String... attrNames) { - if (attrNames == null) { - setRequest(getRequest().toBuilder().attributesToGet((String[]) null).build()); - } else { - setRequest(getRequest().toBuilder().attributesToGet(Arrays.asList(attrNames)).build()); - } - return this; - } - - public Boolean isConsistentRead() { - return getRequest().consistentRead(); - } - - public GetItemSpec withConsistentRead(boolean consistentRead) { - setRequest(getRequest().toBuilder().consistentRead(consistentRead).build()); - return this; - } - - public String getProjectionExpression() { - return getRequest().projectionExpression(); - } - - /** - * When a projection expression is specified, the corresponding name-map can - * optionally be specified via { {@link #withNameMap(Map)}. (Note - * attributes-to-get must not be specified if a projection expression has - * been specified.) - */ - public GetItemSpec withProjectionExpression(String projectionExpression) { - setRequest(getRequest().toBuilder().projectionExpression(projectionExpression).build()); - return this; - } - - public Map nameMap() { - return nameMap; - } - - /** - * Applicable only when an expression has been specified. - * Used to specify the actual values for the attribute-name placeholders, - * where the value in the map can either be string for simple attribute - * name, or a JSON path expression. - */ - public GetItemSpec withNameMap(Map nameMap) { - if (nameMap == null) { - this.nameMap = null; - } else { - this.nameMap = Collections.unmodifiableMap( - new LinkedHashMap(nameMap)); - } - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/ListTablesSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/ListTablesSpec.java deleted file mode 100644 index d14db7007fea..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/ListTablesSpec.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest; - -/** - * Full parameter specification for the ListTables API. - */ -public class ListTablesSpec extends AbstractCollectionSpec { - - public ListTablesSpec() { - super(ListTablesRequest.builder().build()); - } - - public String getExclusiveStartTableName() { - return getRequest().exclusiveStartTableName(); - } - - public ListTablesSpec withExclusiveStartTableName(String exclusiveStartTableName) { - setRequest(getRequest().toBuilder().exclusiveStartTableName(exclusiveStartTableName).build()); - return this; - } - - @Override - public ListTablesSpec withMaxResultSize(Integer maxResultSize) { - setMaxResultSize(maxResultSize); - return this; - } - - @Override - public ListTablesSpec withMaxResultSize(int maxResultSize) { - setMaxResultSize(maxResultSize); - return this; - } - - @Override - public ListTablesSpec withMaxPageSize(Integer maxPageSize) { - setMaxPageSize(maxPageSize); - return this; - } - - @Override - public ListTablesSpec withMaxPageSize(int maxPageSize) { - setMaxPageSize(maxPageSize); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/PutItemSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/PutItemSpec.java deleted file mode 100644 index 86770b9d4ee0..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/PutItemSpec.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.Item; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.ReturnItemCollectionMetrics; -import software.amazon.awssdk.services.dynamodb.model.ReturnValue; - -/** - * Full parameter specification for the PutItem API. - */ -public class PutItemSpec extends AbstractSpec { - private Item item; - private Collection expected; - private Map nameMap; - private Map valueMap; - - public PutItemSpec() { - super(PutItemRequest.builder().build()); - } - - public Item getItem() { - return item; - } - - public PutItemSpec withItem(Item item) { - this.item = item; - return this; - } - - public Collection getExpected() { - return expected; - } - - public PutItemSpec withExpected(Expected... expected) { - if (expected == null) { - this.expected = null; - return this; - } - return withExpected(Arrays.asList(expected)); - } - - public PutItemSpec withExpected(Collection expected) { - if (expected == null) { - this.expected = null; - return this; - } - Set names = new LinkedHashSet(); - for (Expected e : expected) { - names.add(e.getAttribute()); - } - if (names.size() != expected.size()) { - throw new IllegalArgumentException( - "attribute names must not duplicate in the list of expected"); - } - this.expected = Collections.unmodifiableCollection(expected); - return this; - } - - public String getConditionExpression() { - return getRequest().conditionExpression(); - } - - public PutItemSpec withConditionExpression(String conditionExpression) { - setRequest(getRequest().toBuilder().conditionExpression(conditionExpression).build()); - return this; - } - - public Map nameMap() { - return nameMap; - } - - /** - * Applicable only when an expression has been specified. - * Used to specify the actual values for the attribute-name placeholders, - * where the value in the map can either be string for simple attribute - * name, or a JSON path expression. - */ - public PutItemSpec withNameMap(Map nameMap) { - if (nameMap == null) { - this.nameMap = null; - } else { - this.nameMap = Collections.unmodifiableMap( - new LinkedHashMap(nameMap)); - } - return this; - } - - public Map valueMap() { - return valueMap; - } - - /** - * Applicable only when an expression has been specified. Used to - * specify the actual values for the attribute-value placeholders. - */ - public PutItemSpec valueMap(Map valueMap) { - if (valueMap == null) { - this.valueMap = null; - } else { - this.valueMap = Collections.unmodifiableMap( - new LinkedHashMap(valueMap)); - } - return this; - } - - public String getConditionalOperator() { - return getRequest().conditionalOperatorAsString(); - } - - public PutItemSpec withConditionalOperator( - ConditionalOperator conditionalOperator) { - setRequest(getRequest().toBuilder().conditionalOperator(conditionalOperator).build()); - return this; - } - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - public PutItemSpec withReturnConsumedCapacity( - ReturnConsumedCapacity returnConsumedCapacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(returnConsumedCapacity).build()); - return this; - } - - public String getReturnItemCollectionMetrics() { - return getRequest().returnItemCollectionMetricsAsString(); - } - - public PutItemSpec withReturnItemCollectionMetrics( - ReturnItemCollectionMetrics returnItemCollectionMetrics) { - setRequest(getRequest().toBuilder() - .returnItemCollectionMetrics(returnItemCollectionMetrics) - .build()); - return this; - } - - public String getReturnValues() { - return getRequest().returnValuesAsString(); - } - - public PutItemSpec withReturnValues(ReturnValue returnValues) { - setRequest(getRequest().toBuilder().returnValues(returnValues).build()); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/QuerySpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/QuerySpec.java deleted file mode 100644 index 2ddea07383bc..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/QuerySpec.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.QueryFilter; -import software.amazon.awssdk.services.dynamodb.document.RangeKeyCondition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.Select; - -/** - * Full parameter specification for the Query API. - */ -public class QuerySpec extends AbstractCollectionSpec { - private KeyAttribute hashKey; - private RangeKeyCondition rangeKeyCondition; - private Collection queryFilters; - private Map nameMap; - private Map valueMap; - - private Collection exclusiveStartKey; - - public QuerySpec() { - super(QueryRequest.builder().build()); - } - - public KeyAttribute getHashKey() { - return hashKey; - } - - public QuerySpec withHashKey(KeyAttribute hashKey) { - this.hashKey = hashKey; - return this; - } - - public QuerySpec withHashKey(String hashKeyName, Object hashKeyValue) { - this.hashKey = new KeyAttribute(hashKeyName, hashKeyValue); - return this; - } - - public RangeKeyCondition getRangeKeyCondition() { - return rangeKeyCondition; - } - - public QuerySpec withRangeKeyCondition(RangeKeyCondition rangeKeyCondition) { - this.rangeKeyCondition = rangeKeyCondition; - return this; - } - - /** - * When a key condition expression is specified, the corresponding name-map - * and value-map can optionally be specified via {@link #withNameMap(Map)} - * and {@link #valueMap(Map)}. (Note the hash key and range key - * conditions must not be specified if a key condition expression has been - * specified.) - */ - public QuerySpec withKeyConditionExpression(String keyConditionExpression) { - setRequest(getRequest().toBuilder().keyConditionExpression(keyConditionExpression).build()); - return this; - } - - public String getKeyConditionExpression() { - return getRequest().keyConditionExpression(); - } - - public QuerySpec withAttributesToGet(String... attributes) { - setRequest(getRequest().toBuilder().attributesToGet(Arrays.asList(attributes)).build()); - return this; - } - - public List getAttributesToGet() { - return getRequest().attributesToGet(); - } - - public QuerySpec withConditionalOperator(ConditionalOperator op) { - setRequest(getRequest().toBuilder().conditionalOperator(op).build()); - return this; - } - - public String getConditionalOperator() { - return getRequest().conditionalOperatorAsString(); - } - - public QuerySpec withConsistentRead(boolean consistentRead) { - setRequest(getRequest().toBuilder().consistentRead(consistentRead).build()); - return this; - } - - public boolean isConsistentRead() { - return getRequest().consistentRead(); - } - - public QuerySpec withQueryFilters(QueryFilter... queryFilters) { - if (queryFilters == null) { - this.queryFilters = null; - } else { - Set names = new LinkedHashSet(); - for (QueryFilter e : queryFilters) { - names.add(e.getAttribute()); - } - if (names.size() != queryFilters.length) { - throw new IllegalArgumentException( - "attribute names must not duplicate in the list of query filters"); - } - this.queryFilters = Arrays.asList(queryFilters); - } - return this; - } - - public Collection getQueryFilters() { - return queryFilters; - } - - /** - * When a filter expression is specified, the corresponding name-map and - * value-map can optionally be specified via {@link #withNameMap(Map)} and - * {@link #valueMap(Map)}. (Note query filters must not be specified if - * a filter expression has been specified.) - */ - public QuerySpec withFilterExpression(String filterExpression) { - setRequest(getRequest().toBuilder().filterExpression(filterExpression).build()); - return this; - } - - public String getFilterExpression() { - return getRequest().filterExpression(); - } - - /** - * When a projection expression is specified, the corresponding name-map and - * value-map can optionally be specified via {@link #withNameMap(Map)} and - * {@link #valueMap(Map)}. (Note attributes-to-get must not be specified - * if a projection expression has been specified.) - */ - public QuerySpec withProjectionExpression(String projectionExpression) { - setRequest(getRequest().toBuilder().projectionExpression(projectionExpression).build()); - return this; - } - - public String getProjectionExpression() { - return getRequest().projectionExpression(); - } - - public Map nameMap() { - return nameMap; - } - - /** - * Applicable only when an expression has been specified. - * Used to specify the actual values for the attribute-name placeholders, - * where the value in the map can either be string for simple attribute - * name, or a JSON path expression. - */ - public QuerySpec withNameMap(Map nameMap) { - if (nameMap == null) { - this.nameMap = null; - } else { - this.nameMap = Collections.unmodifiableMap(new LinkedHashMap(nameMap)); - } - return this; - } - - public Map valueMap() { - return valueMap; - } - - /** - * Applicable only when an expression has been specified. Used to - * specify the actual values for the attribute-value placeholders. - */ - public QuerySpec valueMap(Map valueMap) { - if (valueMap == null) { - this.valueMap = null; - } else { - this.valueMap = Collections.unmodifiableMap(new LinkedHashMap(valueMap)); - } - return this; - } - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - public QuerySpec withReturnConsumedCapacity( - ReturnConsumedCapacity returnConsumedCapacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(returnConsumedCapacity).build()); - return this; - } - - public QuerySpec withScanIndexForward(boolean scanIndexForward) { - setRequest(getRequest().toBuilder().scanIndexForward(scanIndexForward).build()); - return this; - } - - public boolean isScanIndexForward() { - return getRequest().scanIndexForward(); - } - - public QuerySpec withSelect(Select select) { - setRequest(getRequest().toBuilder().select(select).build()); - return this; - } - - public String select() { - return getRequest().selectAsString(); - } - - // Exclusive start key - - public Collection getExclusiveStartKey() { - return exclusiveStartKey; - } - - public QuerySpec withExclusiveStartKey(KeyAttribute... exclusiveStartKey) { - if (exclusiveStartKey == null) { - this.exclusiveStartKey = null; - } else { - this.exclusiveStartKey = Arrays.asList(exclusiveStartKey); - } - return this; - } - - public QuerySpec withExclusiveStartKey(PrimaryKey exclusiveStartKey) { - if (exclusiveStartKey == null) { - this.exclusiveStartKey = null; - } else { - this.exclusiveStartKey = exclusiveStartKey.getComponents(); - } - return this; - } - - public QuerySpec withExclusiveStartKey( - String hashKeyName, Object hashKeyValue) { - return withExclusiveStartKey(new KeyAttribute(hashKeyName, hashKeyValue)); - } - - public QuerySpec withExclusiveStartKey( - String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return withExclusiveStartKey( - new KeyAttribute(hashKeyName, hashKeyValue), - new KeyAttribute(rangeKeyName, rangeKeyValue)); - } - - // Max result size - - @Override - public QuerySpec withMaxResultSize(Integer maxResultSize) { - setMaxResultSize(maxResultSize); - return this; - } - - @Override - public QuerySpec withMaxResultSize(int maxResultSize) { - setMaxResultSize(maxResultSize); - return this; - } - - @Override - public QuerySpec withMaxPageSize(Integer maxPageSize) { - setMaxPageSize(maxPageSize); - return this; - } - - @Override - public QuerySpec withMaxPageSize(int maxPageSize) { - setMaxPageSize(maxPageSize); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/ScanSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/ScanSpec.java deleted file mode 100644 index e40b43c4a4c9..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/ScanSpec.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.document.ScanFilter; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.Select; - -/** - * API for fully specifying all the parameters of a Table-centric Scan API. - */ -public class ScanSpec extends AbstractCollectionSpec { - private Collection scanFilters; - private Map nameMap; - private Map valueMap; - - private Collection exclusiveStartKey; - - public ScanSpec() { - super(ScanRequest.builder().build()); - } - - /** - * @see ScanRequest#scanFilter() - */ - public Collection scanFilters() { - return scanFilters; - } - - /** - * @see ScanRequest#withScanFilter(Map) - */ - public ScanSpec withScanFilters(ScanFilter... scanFilters) { - if (scanFilters == null) { - this.scanFilters = null; - } else { - Set names = new LinkedHashSet(); - for (ScanFilter e : scanFilters) { - names.add(e.getAttribute()); - } - if (names.size() != scanFilters.length) { - throw new IllegalArgumentException( - "attribute names must not duplicate in the list of scan filters"); - } - this.scanFilters = Arrays.asList(scanFilters); - } - return this; - } - - /** - * AND|OR that applies to all the conditions in the ScanFilters. - * - * @see ScanRequest#getConditionalOperator() - */ - public String getConditionalOperator() { - return getRequest().conditionalOperatorAsString(); - } - - /** - * @see ScanRequest#withConditionalOperator(ConditionalOperator) - */ - public ScanSpec withConditionalOperator(ConditionalOperator op) { - setRequest(getRequest().toBuilder().conditionalOperator(op).build()); - return this; - } - - /** - * @see ScanRequest#getAttributesToGet() - */ - public List getAttributesToGet() { - return getRequest().attributesToGet(); - } - - /** - * @see ScanRequest#withAttributesToGet(String...) - */ - public ScanSpec withAttributesToGet(String... attributes) { - if (attributes == null) { - setRequest(getRequest().toBuilder().attributesToGet((String []) null).build()); - } else { - setRequest(getRequest().toBuilder().attributesToGet(Arrays.asList(attributes)).build()); - } - return this; - } - - /** - * Any query filters will be ignored if a filter expression has been - * specified. When a filter expression is specified, the corresponding - * name-map and value-map can also be specified via - * {@link #withNameMap(Map)} and {@link #valueMap(Map)}. - * - * @see ScanRequest#getFilterExpression() - */ - public String getFilterExpression() { - return getRequest().filterExpression(); - } - - /** - * @see ScanRequest#withFilterExpression(String) - */ - public ScanSpec withFilterExpression(String filterExpression) { - setRequest(getRequest().toBuilder().filterExpression(filterExpression).build()); - return this; - } - - /** - * @see ScanRequest#getProjectionExpression() - */ - public String getProjectionExpression() { - return getRequest().projectionExpression(); - } - - /** - * @see ScanRequest#withProjectionExpression(String) - */ - public ScanSpec withProjectionExpression(String projectionExpression) { - setRequest(getRequest().toBuilder().projectionExpression(projectionExpression).build()); - return this; - } - - /** - * @see ScanRequest#getExpressionAttributeNames() - */ - public Map nameMap() { - return nameMap; - } - - /** - * Applicable only when an expression has been specified. - * Used to specify the actual values for the attribute-name placeholders, - * where the value in the map can either be string for simple attribute - * name, or a JSON path expression. - * - * @see ScanRequest#withExpressionAttributeNames(Map) - */ - public ScanSpec withNameMap(Map nameMap) { - if (nameMap == null) { - this.nameMap = null; - } else { - this.nameMap = Collections.unmodifiableMap(new LinkedHashMap(nameMap)); - } - return this; - } - - /** - * @see ScanRequest#getExpressionAttributeValues() - */ - public Map valueMap() { - return valueMap; - } - - /** - * Applicable only when an expression has been specified. Used to - * specify the actual values for the attribute-value placeholders. - * - * @see ScanRequest#withExpressionAttributeValues(Map) - */ - public ScanSpec valueMap(Map valueMap) { - if (valueMap == null) { - this.valueMap = null; - } else { - this.valueMap = Collections.unmodifiableMap(new LinkedHashMap(valueMap)); - } - return this; - } - - /** - * @see ScanRequest#getReturnConsumedCapacity() - */ - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - /** - * @see ScanRequest#withReturnConsumedCapacity(ReturnConsumedCapacity) - */ - public ScanSpec withReturnConsumedCapacity(ReturnConsumedCapacity capacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(capacity).build()); - return this; - } - - /** - * Specifies the attributes to be returned. - * - * @see ScanRequest#select() - */ - // ALL_ATTRIBUTES | ALL_PROJECTED_ATTRIBUTES | SPECIFIC_ATTRIBUTES | COUNT - public String select() { - return getRequest().selectAsString(); - } - - /** - * @see ScanRequest#withSelect(Select) - */ - public ScanSpec withSelect(Select select) { - setRequest(getRequest().toBuilder().select(select).build()); - return this; - } - - /** - * @see ScanRequest#segment() - */ - public Integer segment() { - return getRequest().segment(); - } - - /** - * @see ScanRequest#withSegment(Integer) - */ - public ScanSpec withSegment(Integer segment) { - setRequest(getRequest().toBuilder().segment(segment).build()); - return this; - } - - /** - * @see ScanRequest#getTotalSegments() - */ - public Integer getTotalSegments() { - return getRequest().totalSegments(); - } - - /** - * @see ScanRequest#withTotalSegments(Integer) - */ - public ScanSpec withTotalSegments(Integer totalSegments) { - setRequest(getRequest().toBuilder().totalSegments(totalSegments).build()); - return this; - } - - /** - * @see ScanRequest#isConsistentRead() - */ - public Boolean isConsistentRead() { - return getRequest().consistentRead(); - } - - /** - * @see ScanRequest#withConsistentRead(Boolean) - */ - public ScanSpec withConsistentRead(Boolean consistentRead) { - setRequest(getRequest().toBuilder().consistentRead(consistentRead).build()); - return this; - } - - // Exclusive start key - - /** - * @see ScanRequest#getExclusiveStartKey() - */ - public Collection getExclusiveStartKey() { - return exclusiveStartKey; - } - - /** - * @see ScanRequest#withExclusiveStartKey(Map) - */ - public ScanSpec withExclusiveStartKey(KeyAttribute... exclusiveStartKey) { - if (exclusiveStartKey == null) { - this.exclusiveStartKey = null; - } else { - this.exclusiveStartKey = Arrays.asList(exclusiveStartKey); - } - return this; - } - - /** - * @see ScanRequest#withExclusiveStartKey(Map) - */ - public ScanSpec withExclusiveStartKey(PrimaryKey exclusiveStartKey) { - if (exclusiveStartKey == null) { - this.exclusiveStartKey = null; - } else { - this.exclusiveStartKey = exclusiveStartKey.getComponents(); - } - return this; - } - - /** - * @see ScanRequest#withExclusiveStartKey(Map) - */ - public ScanSpec withExclusiveStartKey( - String hashKeyName, Object hashKeyValue) { - return withExclusiveStartKey(new KeyAttribute(hashKeyName, hashKeyValue)); - } - - /** - * @see ScanRequest#withExclusiveStartKey(Map) - */ - public ScanSpec withExclusiveStartKey( - String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - return withExclusiveStartKey( - new KeyAttribute(hashKeyName, hashKeyValue), - new KeyAttribute(rangeKeyName, rangeKeyValue)); - } - - // Max result size - - @Override - public ScanSpec withMaxResultSize(Integer maxResultSize) { - setMaxResultSize(maxResultSize); - return this; - } - - @Override - public ScanSpec withMaxResultSize(int maxResultSize) { - setMaxResultSize(maxResultSize); - return this; - } - - @Override - public ScanSpec withMaxPageSize(Integer maxPageSize) { - setMaxPageSize(maxPageSize); - return this; - } - - @Override - public ScanSpec withMaxPageSize(int maxPageSize) { - setMaxPageSize(maxPageSize); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/UpdateItemSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/UpdateItemSpec.java deleted file mode 100644 index b2beecab7e77..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/UpdateItemSpec.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.AttributeUpdate; -import software.amazon.awssdk.services.dynamodb.document.Expected; -import software.amazon.awssdk.services.dynamodb.document.KeyAttribute; -import software.amazon.awssdk.services.dynamodb.document.PrimaryKey; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.ReturnItemCollectionMetrics; -import software.amazon.awssdk.services.dynamodb.model.ReturnValue; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; - -/** - * Full parameter specification for the UpdateItem API. - */ -public class UpdateItemSpec extends AbstractSpecWithPrimaryKey { - private List attributes; - private Collection expected; - - private Map nameMap; - private Map valueMap; - - public UpdateItemSpec() { - super(UpdateItemRequest.builder().build()); - } - - @Override - public UpdateItemSpec withPrimaryKey(KeyAttribute... components) { - super.withPrimaryKey(components); - return this; - } - - @Override - public UpdateItemSpec withPrimaryKey(PrimaryKey primaryKey) { - super.withPrimaryKey(primaryKey); - return this; - } - - @Override - public UpdateItemSpec withPrimaryKey(String hashKeyName, Object hashKeyValue) { - super.withPrimaryKey(hashKeyName, hashKeyValue); - return this; - } - - @Override - public UpdateItemSpec withPrimaryKey(String hashKeyName, Object hashKeyValue, - String rangeKeyName, Object rangeKeyValue) { - super.withPrimaryKey(hashKeyName, hashKeyValue, rangeKeyName, rangeKeyValue); - return this; - } - - public List getAttributeUpdate() { - return attributes; - } - - public UpdateItemSpec withAttributeUpdate( - List attributeUpdates) { - this.attributes = attributeUpdates; - return this; - } - - public UpdateItemSpec withAttributeUpdate( - AttributeUpdate... attributeUpdates) { - this.attributes = new ArrayList(Arrays.asList(attributeUpdates)); - return this; - } - - public UpdateItemSpec addAttributeUpdate(AttributeUpdate attributeUpdate) { - if (null == this.attributes) { - this.attributes = new ArrayList(); - } - this.attributes.add(attributeUpdate); - return this; - } - - public UpdateItemSpec clearAttributeUpdate() { - this.attributes = null; - return this; - } - - public Collection getExpected() { - return expected; - } - - public UpdateItemSpec withExpected(Expected... expected) { - if (expected == null) { - this.expected = null; - return this; - } - return withExpected(Arrays.asList(expected)); - } - - public UpdateItemSpec withExpected(Collection expected) { - if (expected == null) { - this.expected = null; - return this; - } - Set names = new LinkedHashSet(); - for (Expected e : expected) { - names.add(e.getAttribute()); - } - if (names.size() != expected.size()) { - throw new IllegalArgumentException( - "attribute names must not duplicate in the list of expected"); - } - this.expected = Collections.unmodifiableCollection(expected); - return this; - } - - public String getUpdateExpression() { - return getRequest().updateExpression(); - } - - public UpdateItemSpec withUpdateExpression(String updateExpression) { - setRequest(getRequest().toBuilder().updateExpression(updateExpression).build()); - return this; - } - - public String getConditionExpression() { - return getRequest().conditionExpression(); - } - - public UpdateItemSpec withConditionExpression(String conditionExpression) { - setRequest(getRequest().toBuilder().conditionExpression(conditionExpression).build()); - return this; - } - - public Map nameMap() { - return nameMap; - } - - /** - * Applicable only when an expression has been specified. - * Used to specify the actual values for the attribute-name placeholders, - * where the value in the map can either be string for simple attribute - * name, or a JSON path expression. - */ - public UpdateItemSpec withNameMap(Map nameMap) { - if (nameMap == null) { - this.nameMap = null; - } else { - this.nameMap = Collections.unmodifiableMap( - new LinkedHashMap(nameMap)); - } - return this; - } - - public Map valueMap() { - return valueMap; - } - - /** - * Applicable only when an expression has been specified. Used to - * specify the actual values for the attribute-value placeholders. - */ - public UpdateItemSpec valueMap(Map valueMap) { - if (valueMap == null) { - this.valueMap = null; - } else { - this.valueMap = Collections.unmodifiableMap( - new LinkedHashMap(valueMap)); - } - return this; - } - - public String getConditionalOperator() { - return getRequest().conditionalOperatorAsString(); - } - - public String getReturnConsumedCapacity() { - return getRequest().returnConsumedCapacityAsString(); - } - - public UpdateItemSpec withReturnConsumedCapacity( - String returnConsumedCapacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(returnConsumedCapacity).build()); - return this; - } - - public UpdateItemSpec withReturnConsumedCapacity( - ReturnConsumedCapacity returnConsumedCapacity) { - setRequest(getRequest().toBuilder().returnConsumedCapacity(returnConsumedCapacity).build()); - return this; - } - - public String getReturnItemCollectionMetrics() { - return getRequest().returnItemCollectionMetricsAsString(); - } - - public UpdateItemSpec withReturnItemCollectionMetrics( - ReturnItemCollectionMetrics returnItemCollectionMetrics) { - setRequest(getRequest().toBuilder().returnItemCollectionMetrics(returnItemCollectionMetrics).build()); - return this; - } - - public UpdateItemSpec withReturnItemCollectionMetrics( - String returnItemCollectionMetrics) { - setRequest(getRequest().toBuilder().returnItemCollectionMetrics(returnItemCollectionMetrics).build()); - return this; - } - - public String getReturnValues() { - return getRequest().returnValuesAsString(); - } - - public UpdateItemSpec withReturnValues(ReturnValue returnValues) { - setRequest(getRequest().toBuilder().returnValues(returnValues).build()); - return this; - } - - public UpdateItemSpec withReturnValues(String returnValues) { - setRequest(getRequest().toBuilder().returnValues(returnValues).build()); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/UpdateTableSpec.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/UpdateTableSpec.java deleted file mode 100644 index daba71deb2b4..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/spec/UpdateTableSpec.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.spec; - -import java.util.Collection; -import java.util.List; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexUpdate; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.UpdateTableRequest; - -/** - * Full parameter specification for the UpdateTable API. - */ -public class UpdateTableSpec extends AbstractSpec { - public UpdateTableSpec() { - super(UpdateTableRequest.builder().build()); - } - - public ProvisionedThroughput getProvisionedThroughput() { - return getRequest().provisionedThroughput(); - } - - public UpdateTableSpec withProvisionedThroughput( - ProvisionedThroughput provisionedThroughput) { - setRequest(getRequest().toBuilder().provisionedThroughput(provisionedThroughput).build()); - return this; - } - - public List getAttributeDefinitions() { - return getRequest().attributeDefinitions(); - } - - public UpdateTableSpec withAttributeDefinitions( - AttributeDefinition... attributeDefinitions) { - setRequest(getRequest().toBuilder().attributeDefinitions(attributeDefinitions).build()); - return this; - } - - public UpdateTableSpec withAttributeDefinitions( - Collection attributeDefinitions) { - setRequest(getRequest().toBuilder().attributeDefinitions(attributeDefinitions).build()); - return this; - } - - public UpdateTableSpec withGlobalSecondaryIndexUpdates( - GlobalSecondaryIndexUpdate... globalSecondaryIndexUpdates) { - setRequest(getRequest().toBuilder().globalSecondaryIndexUpdates( - globalSecondaryIndexUpdates).build()); - return this; - } - - public UpdateTableSpec withGlobalSecondaryIndexUpdates( - Collection globalSecondaryIndexUpdates) { - setRequest(getRequest().toBuilder().globalSecondaryIndexUpdates( - globalSecondaryIndexUpdates) - .build()); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentArrayList.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentArrayList.java deleted file mode 100644 index 3e3ea920d218..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentArrayList.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - -import java.util.ArrayList; -import java.util.Collection; - -/** - * Utility subclass of {@link ArrayList} that supports fluent API. - */ -public class FluentArrayList extends ArrayList { - private static final long serialVersionUID = -8269850815375778149L; - - public FluentArrayList(int initialCapacity) { - super(initialCapacity); - } - - public FluentArrayList() { - super(); - } - - // @SafeVarargs - public FluentArrayList(E... elements) { - appendAll(elements); - } - - public FluentArrayList(Collection c) { - super(c); - } - - /** - * Fluent method to add the specified element to this list. - */ - public FluentArrayList append(E e) { - super.add(e); - return this; - } - - /** - * Fluent method to remove the specified element from this list. - */ - public FluentArrayList delete(Object o) { - super.remove(o); - return this; - } - - /** - * Fluent method to add the elements from the specified collection to this - * list. - */ - public FluentArrayList appendAll(Collection c) { - super.addAll(c); - return this; - } - - /** - * Fluent method to add the elements to this list. - */ - // @SuppressWarnings("unchecked") - public FluentArrayList appendAll(E... elements) { - if (elements != null) { - for (E e : elements) { - add(e); - } - } - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentHashMap.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentHashMap.java deleted file mode 100644 index 8574d7c7e106..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentHashMap.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - -import java.util.LinkedHashMap; - -/** - * Utility subclass of {@link LinkedHashMap} that supports fluent API. - */ -public class FluentHashMap extends LinkedHashMap { - private static final long serialVersionUID = 4857340227048063855L; - - /** - * Fluent method to remove the specified key from this map. - */ - public FluentHashMap delete(Object key) { - remove(key); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentHashSet.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentHashSet.java deleted file mode 100644 index 17b6a1bc4fac..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/FluentHashSet.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - -import java.util.Collection; -import java.util.LinkedHashSet; - -/** - * Utility subclass of {@link LinkedHashSet} that supports fluent API. - */ -public class FluentHashSet extends LinkedHashSet { - private static final long serialVersionUID = -549868294257559427L; - - public FluentHashSet() { - super(); - } - - public FluentHashSet(Collection c) { - super(c); - } - - // @SafeVarargs - public FluentHashSet(E... elements) { - withAll(elements); - } - - public FluentHashSet(int initialCapacity, float loadFactor) { - super(initialCapacity, loadFactor); - } - - public FluentHashSet(int initialCapacity) { - super(initialCapacity); - } - - /** - * Fluent method to add the specified element to this set. - */ - public FluentHashSet with(E e) { - super.add(e); - return this; - } - - /** - * Fluent method to add the elements from the specified collection to this - * set. - */ - public FluentHashSet withAll(Collection c) { - super.addAll(c); - return this; - } - - /** - * Fluent method to add the elements to this set. - */ - // @SuppressWarnings("unchecked") - public FluentHashSet withAll(E... elements) { - if (elements != null) { - for (E e : elements) { - add(e); - } - } - return this; - } - - /** - * Fluent method to remove the specified element from this set. - */ - public FluentHashSet delete(Object o) { - super.remove(o); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/NameMap.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/NameMap.java deleted file mode 100644 index a9be53a62b5e..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/NameMap.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - - -/** - * Utility class for name maps. - */ -public class NameMap extends FluentHashMap { - private static final long serialVersionUID = 1L; - - /** - * Fluent method to sets the given key (attribute name place holder) to the - * specified value (the actual attribute name.) - */ - public NameMap with(String key, String value) { - super.put(key, value); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueList.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueList.java deleted file mode 100644 index c5fe0cb78bdb..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueList.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; - -/** - * Utility value list. - */ -public class ValueList extends FluentArrayList { - private static final long serialVersionUID = 1L; - - public ValueList(int initialCapacity) { - super(initialCapacity); - } - - public ValueList(Object... elements) { - super(elements); - } - - public ValueList() { - super(); - } - - public ValueList(Collection c) { - super(c); - } - - public ValueList appendAll(Object... elements) { - super.appendAll(elements); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendString(String val) { - super.append(val); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendNumber(BigDecimal val) { - super.append(val); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendNumber(Number val) { - super.append(InternalUtils.toBigDecimal(val)); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendInt(int val) { - return appendNumber(Integer.valueOf(val)); - } - - /** - * Appends the given value to this list. - */ - public ValueList appendLong(long val) { - return appendNumber(Long.valueOf(val)); - } - - /** - * Appends the given value to this list. - */ - public ValueList appendBinary(byte[] val) { - super.append(val); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendStringSet(Set val) { - super.append(val); - return this; - } - - /** - * Appends the given values to this list as a string set. - */ - public ValueList appendStringSet(String... val) { - super.append(new LinkedHashSet(Arrays.asList(val))); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendNumberSet(Set val) { - super.append(val); - return this; - } - - /** - * Appends the given value to this list as a set of BigDecimals. - */ - public ValueList appendNumberSet(BigDecimal... val) { - super.append(new LinkedHashSet(Arrays.asList(val))); - return this; - } - - /** - * Appends the given values to this list as a number set. - */ - public ValueList appendNumberSet(Number... val) { - super.append(InternalUtils.toBigDecimalSet(val)); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendBinarySet(Set val) { - super.append(val); - return this; - } - - /** - * Appends the given values to this list as a set of byte arrays. - */ - public ValueList appendBinarySet(byte[]... val) { - super.append(new LinkedHashSet(Arrays.asList(val))); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendList(List val) { - super.append(new ArrayList(val)); - return this; - } - - /** - * Appends the given values to this list as a list. - */ - public ValueList appendList(Object... vals) { - super.append(new ArrayList(Arrays.asList(vals))); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendMap(Map val) { - super.append(val); - return this; - } - - /** - * Appends the given value to this list. - */ - public ValueList appendBoolean(boolean val) { - super.append(Boolean.valueOf(val)); - return this; - } - - /** - * Appends a null value to this list. - */ - public ValueList appendNull() { - super.append(null); - return this; - } - - /** - * Appends the given value to this list. A value can be a - *
      - *
    • Number
    • - *
    • String
    • - *
    • binary (ie byte array or byte buffer)
    • - *
    • boolean
    • - *
    • null
    • - *
    • list (of any of the types on this list)
    • - *
    • map (append string key to value of any of the types on this list)
    • - *
    • set (of any of the types on this list)
    • - *
    - */ - public ValueList append(Object val) { - if (val == this) { - throw new IllegalArgumentException("Self reference is not allowed"); - } - // TODO: fail fast if val is not a supported type - super.append(val); - return this; - } - -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueMap.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueMap.java deleted file mode 100644 index c11ee95d5de8..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueMap.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.core.util.json.JacksonUtils; -import software.amazon.awssdk.services.dynamodb.document.internal.InternalUtils; -import software.amazon.awssdk.services.dynamodb.document.internal.ItemValueConformer; - - -/** - * Utility class for value maps. - */ -public class ValueMap extends FluentHashMap { - private static final long serialVersionUID = 1L; - private static final ItemValueConformer VALUE_CONFORMER = new ItemValueConformer(); - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withString(String key, String val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withNumber(String key, BigDecimal val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withNumber(String key, Number val) { - super.put(key, InternalUtils.toBigDecimal(val)); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withInt(String key, int val) { - return withNumber(key, Integer.valueOf(val)); - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withLong(String key, long val) { - return withNumber(key, Long.valueOf(val)); - } - - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withBinary(String key, byte[] val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withStringSet(String key, Set val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withStringSet(String key, String... val) { - super.put(key, new LinkedHashSet(Arrays.asList(val))); - return this; - } - - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withNumberSet(String key, Set val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withNumberSet(String key, BigDecimal... val) { - super.put(key, new LinkedHashSet(Arrays.asList(val))); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withNumberSet(String key, Number... val) { - super.put(key, InternalUtils.toBigDecimalSet(val)); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withBinarySet(String key, Set val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withBinarySet(String key, byte[]... val) { - super.put(key, new LinkedHashSet(Arrays.asList(val))); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withList(String key, List val) { - super.put(key, val == null ? null : new ArrayList(val)); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given values as a list. - */ - public ValueMap withList(String key, Object... vals) { - super.put(key, - vals == null ? null : new ArrayList(Arrays.asList(vals))); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * given value. - */ - public ValueMap withMap(String key, Map val) { - super.put(key, val); - return this; - } - - /** - * Sets the value of the specified key in the current ValueMap to the - * boolean value. - */ - public ValueMap withBoolean(String key, boolean val) { - super.put(key, Boolean.valueOf(val)); - return this; - } - - /** - * Sets the value of the specified key to null. - */ - public ValueMap withNull(String key) { - super.put(key, null); - return this; - } - - /** - * Sets the value of the specified key to an object represented by the JSON - * structure passed. - */ - public ValueMap withJson(String key, String jsonValue) { - super.put(key, VALUE_CONFORMER.transform(JacksonUtils.fromJsonString(jsonValue, Object.class))); - return this; - } - - /** - * Sets the value of the specified key to the given value. A - * value can be a - *
      - *
    • Number
    • - *
    • String
    • DefaultMetricCollectorFactory - *
    • binary (ie byte array or byte buffer)
    • - *
    • boolean
    • - *
    • null
    • - *
    • list (of any of the types on this list)
    • - *
    • map (with string key to value of any of the types on this list)
    • - *
    • set (of any of the types on this list)
    • - *
    - */ - public ValueMap with(String key, Object val) { - if (val == this) { - throw new IllegalArgumentException("Self reference is not allowed"); - } - // TODO: fail fast if val is not a supported type - super.put(key, val); - return this; - } -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueMapAndWithJsonSupportTest.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueMapAndWithJsonSupportTest.java deleted file mode 100644 index 022ae23ec5b1..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/document/utils/ValueMapAndWithJsonSupportTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document.utils; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.core.IsCollectionContaining.hasItems; -import static org.junit.Assert.assertThat; - -import java.util.List; -import java.util.Map; -import org.junit.Test; -import software.amazon.awssdk.core.exception.SdkClientException; - - -public class ValueMapAndWithJsonSupportTest { - - private static final String NO_JSON_STRING = "nojson"; - private static final String KEY = "somekey"; - - @Test(expected = SdkClientException.class) - public void valueMapCreationshouldFailIfNoJsonstringIsUsedAsValue() { - new ValueMap().withJson("a", NO_JSON_STRING); - } - - @Test - @SuppressWarnings("unchecked") - public void valueMapShouldReturnAProperDeserializedJsonMap() { - String json = "{ \"fruit\" : \"pear\" , \"color\" : \"green\" }"; - - ValueMap valueMap = new ValueMap().withJson(KEY, json); - Map actual = (Map) valueMap.get(KEY); - - assertThat(actual.size(), is(2)); - assertThat((String) actual.get("fruit"), is("pear")); - assertThat((String) actual.get("color"), is("green")); - } - - @Test - @SuppressWarnings("unchecked") - public void valueMapShouldReturnAProperDeserializedJsonList() { - String json = "[\"red\",\"green\",\"blue\"]"; - - ValueMap valueMap = new ValueMap().withJson(KEY, json); - List actual = (List) valueMap.get(KEY); - - assertThat(actual.size(), is(3)); - assertThat(actual, hasItems("red", "green", "blue")); - } - -} diff --git a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/util/TableUtils.java b/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/util/TableUtils.java deleted file mode 100644 index c85b577d77b0..000000000000 --- a/test/dynamodbdocument-v1/src/test/java/software/amazon/awssdk/services/dynamodb/util/TableUtils.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; - -/** - * Utility methods for working with DynamoDB tables. - * - *
    - * // ... create DynamoDB table ...
    - * try {
    - *     waitUntilActive(dynamoDB, myTableName());
    - * } catch (SdkClientException e) {
    - *     // table didn't become active
    - * }
    - * // ... start making calls to table ...
    - * 
    - */ -public class TableUtils { - - private static final int DEFAULT_WAIT_TIMEOUT = 10 * 60 * 1000; - private static final int DEFAULT_WAIT_INTERVAL = 20 * 1000; - /** - * The logging utility. - */ - private static final Logger log = LoggerFactory.getLogger(TableUtils.class); - - /** - * Waits up to 10 minutes for a specified DynamoDB table to resolve, - * indicating that it exists. If the table doesn't return a result after - * this time, a SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table being resolved. - * - * @throws SdkClientException - * If the specified table does not resolve before this method - * times out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * resolve. - */ - public static void waitUntilExists(final DynamoDbClient dynamo, final String tableName) - throws InterruptedException { - waitUntilExists(dynamo, tableName, DEFAULT_WAIT_TIMEOUT, DEFAULT_WAIT_INTERVAL); - } - - /** - * Waits up to a specified amount of time for a specified DynamoDB table to - * resolve, indicating that it exists. If the table doesn't return a result - * after this time, a SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table being resolved. - * @param timeout - * The maximum number of milliseconds to wait. - * @param interval - * The poll interval in milliseconds. - * - * @throws SdkClientException - * If the specified table does not resolve before this method - * times out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * resolve. - */ - public static void waitUntilExists(final DynamoDbClient dynamo, final String tableName, final int timeout, - final int interval) throws InterruptedException { - TableDescription table = waitForTableDescription(dynamo, tableName, null, timeout, interval); - - if (table == null) { - throw SdkClientException.builder().message("Table " + tableName + " never returned a result").build(); - } - } - - /** - * Waits up to 10 minutes for a specified DynamoDB table to move into the - * ACTIVE state. If the table does not exist or does not - * transition to the ACTIVE state after this time, then - * SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table whose status is being checked. - * - * @throws TableNeverTransitionedToStateException - * If the specified table does not exist or does not transition - * into the ACTIVE state before this method times - * out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * transition into the ACTIVE state. - */ - public static void waitUntilActive(final DynamoDbClient dynamo, final String tableName) - throws InterruptedException, TableNeverTransitionedToStateException { - waitUntilActive(dynamo, tableName, DEFAULT_WAIT_TIMEOUT, DEFAULT_WAIT_INTERVAL); - } - - /** - * Waits up to a specified amount of time for a specified DynamoDB table to - * move into the ACTIVE state. If the table does not exist or - * does not transition to the ACTIVE state after this time, - * then a SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table whose status is being checked. - * @param timeout - * The maximum number of milliseconds to wait. - * @param interval - * The poll interval in milliseconds. - * - * @throws TableNeverTransitionedToStateException - * If the specified table does not exist or does not transition - * into the ACTIVE state before this method times - * out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * transition into the ACTIVE state. - */ - public static void waitUntilActive(final DynamoDbClient dynamo, final String tableName, final int timeout, - final int interval) throws InterruptedException, TableNeverTransitionedToStateException { - TableDescription table = waitForTableDescription(dynamo, tableName, TableStatus.ACTIVE, timeout, interval); - - if (table == null || !table.tableStatus().equals(TableStatus.ACTIVE)) { - throw new TableNeverTransitionedToStateException(tableName, TableStatus.ACTIVE); - } - } - - /** - * Wait for the table to reach the desired status and returns the table - * description - * - * @param dynamo - * Dynamo client to use - * @param tableName - * Table name to poll status of - * @param desiredStatus - * Desired {@link TableStatus} to wait for. If null this method - * simply waits until DescribeTable returns something non-null - * (i.e. any status) - * @param timeout - * Timeout in milliseconds to continue to poll for desired status - * @param interval - * Time to wait in milliseconds between poll attempts - * @return Null if DescribeTables never returns a result, otherwise the - * result of the last poll attempt (which may or may not have the - * desired state) - * @throws {@link - * IllegalArgumentException} If timeout or interval is invalid - */ - private static TableDescription waitForTableDescription(final DynamoDbClient dynamo, final String tableName, - TableStatus desiredStatus, final int timeout, final int interval) - throws InterruptedException, IllegalArgumentException { - if (timeout < 0) { - throw new IllegalArgumentException("Timeout must be >= 0"); - } - if (interval <= 0 || interval >= timeout) { - throw new IllegalArgumentException("Interval must be > 0 and < timeout"); - } - long startTime = System.currentTimeMillis(); - long endTime = startTime + timeout; - - TableDescription table = null; - while (System.currentTimeMillis() < endTime) { - try { - table = dynamo.describeTable(DescribeTableRequest.builder().tableName(tableName).build()).table(); - if (desiredStatus == null || table.tableStatus().equals(desiredStatus)) { - return table; - - } - } catch (ResourceNotFoundException rnfe) { - // ResourceNotFound means the table doesn't exist yet, - // so ignore this error and just keep polling. - } - - Thread.sleep(interval); - } - return table; - } - - /** - * Creates the table and ignores any errors if it already exists. - * @param dynamo The Dynamo client to use. - * @param createTableRequest The create table request. - * @return True if created, false otherwise. - */ - public static boolean createTableIfNotExists(final DynamoDbClient dynamo, final CreateTableRequest createTableRequest) { - try { - dynamo.createTable(createTableRequest); - return true; - } catch (final ResourceInUseException e) { - if (log.isTraceEnabled()) { - log.trace("Table " + createTableRequest.tableName() + " already exists", e); - } - } - return false; - } - - /** - * Deletes the table and ignores any errors if it doesn't exist. - * @param dynamo The Dynamo client to use. - * @param deleteTableRequest The delete table request. - * @return True if deleted, false otherwise. - */ - public static boolean deleteTableIfExists(final DynamoDbClient dynamo, final DeleteTableRequest deleteTableRequest) { - try { - dynamo.deleteTable(deleteTableRequest); - return true; - } catch (final ResourceNotFoundException e) { - if (log.isTraceEnabled()) { - log.trace("Table " + deleteTableRequest.tableName() + " does not exist", e); - } - } - return false; - } - - /** - * Thrown by {@link TableUtils} when a table never reaches a desired state - */ - public static class TableNeverTransitionedToStateException extends SdkClientException { - - private static final long serialVersionUID = 8920567021104846647L; - - public TableNeverTransitionedToStateException(String tableName, TableStatus desiredStatus) { - super(SdkClientException.builder() - .message("Table " + tableName + " never transitioned to desired state of " + - desiredStatus.toString())); - } - - } - -} diff --git a/test/dynamodbmapper-v1/pom.xml b/test/dynamodbmapper-v1/pom.xml deleted file mode 100644 index 086abd733ae6..000000000000 --- a/test/dynamodbmapper-v1/pom.xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - 4.0.0 - - aws-sdk-java-pom - software.amazon.awssdk - 2.10.7-SNAPSHOT - ../../pom.xml - - dynamodbmapper-v1 - AWS Java SDK :: Test :: Amazon DynamoDB Mapper v1 - DynamoDB Mapper largely unchanged from v1. The v1 Mapper is kept for testing purposes only. All classes are in the test directories to prevent use in application code. - https://aws.amazon.com/sdkforjava - - - - - software.amazon.awssdk - bom-internal - ${project.version} - pom - import - - - - - - - software.amazon.awssdk - auth - ${awsjavasdk.version} - test - - - software.amazon.awssdk - regions - ${awsjavasdk.version} - test - - - software.amazon.awssdk - annotations - ${awsjavasdk.version} - test - - - software.amazon.awssdk - utils - ${awsjavasdk.version} - test - - - software.amazon.awssdk - sdk-core - ${awsjavasdk.version} - test - - - software.amazon.awssdk - aws-core - ${awsjavasdk.version} - test - - - test-utils - software.amazon.awssdk - ${awsjavasdk.version} - test - - - org.assertj - assertj-core - test - - - dynamodb - software.amazon.awssdk - ${awsjavasdk.version} - test - - - s3 - software.amazon.awssdk - ${awsjavasdk.version} - test - - - service-test-utils - software.amazon.awssdk - ${awsjavasdk.version} - test - - - junit - junit - test - - - mockito-core - org.mockito - test - - - diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/DynamoDBMapperIntegrationTestBase.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/DynamoDBMapperIntegrationTestBase.java deleted file mode 100644 index 2e47fbc9e3f5..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/DynamoDBMapperIntegrationTestBase.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import java.nio.ByteBuffer; -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.pojos.BinaryAttributeByteBufferClass; -import utils.test.util.DynamoDBIntegrationTestBase; -import utils.test.util.DynamoDBTestBase; - -public class DynamoDBMapperIntegrationTestBase extends DynamoDBIntegrationTestBase { - - public static void setUpMapperTestBase() { - DynamoDBTestBase.setUpTestBase(); - } - - /* - * Utility methods - */ - protected static BinaryAttributeByteBufferClass getUniqueByteBufferObject(int contentLength) { - BinaryAttributeByteBufferClass obj = new BinaryAttributeByteBufferClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBinaryAttribute(ByteBuffer.wrap(generateByteArray(contentLength))); - Set byteBufferSet = new HashSet(); - byteBufferSet.add(ByteBuffer.wrap(generateByteArray(contentLength))); - obj.setBinarySetAttribute(byteBufferSet); - return obj; - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/GsiAlwaysUpdateIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/GsiAlwaysUpdateIntegrationTest.java deleted file mode 100644 index 99836b9b2813..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/GsiAlwaysUpdateIntegrationTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import static org.junit.Assert.assertNotEquals; - -import java.util.UUID; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTableMapper; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; -import software.amazon.awssdk.services.dynamodb.pojos.GsiWithAlwaysUpdateTimestamp; -import software.amazon.awssdk.testutils.Waiter; - -public class GsiAlwaysUpdateIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String TABLE_NAME = - GsiAlwaysUpdateIntegrationTest.class.getSimpleName() + "-" + System.currentTimeMillis(); - - private DynamoDbClient ddb; - private DynamoDbTableMapper mapper; - - @Before - public void setup() { - ddb = DynamoDbClient.builder() - .region(Region.US_WEST_2) - .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) - .build(); - mapper = new DynamoDbMapper(ddb, DynamoDbMapperConfig.builder() - .withTableNameOverride(new DynamoDbMapperConfig.TableNameOverride(TABLE_NAME)) - .build()).newTableMapper(GsiWithAlwaysUpdateTimestamp.class); - mapper.createTable(ProvisionedThroughput.builder().readCapacityUnits(5L).writeCapacityUnits(5L).build()); - Waiter.run(() -> ddb.describeTable(r -> r.tableName(TABLE_NAME))) - .ignoringException(ResourceNotFoundException.class) - .until(r -> r.table().tableStatus() == TableStatus.ACTIVE) - .orFail(); - } - - @After - public void tearDown() { - mapper.deleteTableIfExists(); - Waiter.run(() -> ddb.describeTable(r -> r.tableName(TABLE_NAME))) - .untilException(ResourceNotFoundException.class) - .orFail(); - } - - @Test - public void pojoWithAlwaysGenerateGsi_SavesCorrectly() throws InterruptedException { - final String hashKey = UUID.randomUUID().toString(); - final String rangeKey = UUID.randomUUID().toString(); - - mapper.save(new GsiWithAlwaysUpdateTimestamp() - .setHashKey(hashKey) - .setRangeKey(rangeKey)); - final GsiWithAlwaysUpdateTimestamp created = mapper.load(hashKey, rangeKey); - // Have to store it since the mapper will auto update any generated values in the saved object. - Long createdDate = created.getLastModifiedDate(); - // Need to wait a bit for the timestamps to actually be different - Thread.sleep(1000); - mapper.save(created); - final GsiWithAlwaysUpdateTimestamp updated = mapper.load(hashKey, rangeKey); - assertNotEquals(createdDate, updated.getLastModifiedDate()); - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/TableUtils.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/TableUtils.java deleted file mode 100644 index 87919310b889..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/TableUtils.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; - -/** - * Utility methods for working with DynamoDB tables. - * - *
    - * // ... create DynamoDB table ...
    - * try {
    - *     waitUntilActive(dynamoDB, myTableName());
    - * } catch (SdkClientException e) {
    - *     // table didn't become active
    - * }
    - * // ... start making calls to table ...
    - * 
    - */ -public class TableUtils { - - private static final int DEFAULT_WAIT_TIMEOUT = 10 * 60 * 1000; - private static final int DEFAULT_WAIT_INTERVAL = 20 * 1000; - /** - * The logging utility. - */ - private static final Logger log = LoggerFactory.getLogger(TableUtils.class); - - /** - * Waits up to 10 minutes for a specified DynamoDB table to resolve, - * indicating that it exists. If the table doesn't return a result after - * this time, a SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table being resolved. - * - * @throws SdkClientException - * If the specified table does not resolve before this method - * times out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * resolve. - */ - public static void waitUntilExists(final DynamoDbClient dynamo, final String tableName) - throws InterruptedException { - waitUntilExists(dynamo, tableName, DEFAULT_WAIT_TIMEOUT, DEFAULT_WAIT_INTERVAL); - } - - /** - * Waits up to a specified amount of time for a specified DynamoDB table to - * resolve, indicating that it exists. If the table doesn't return a result - * after this time, a SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table being resolved. - * @param timeout - * The maximum number of milliseconds to wait. - * @param interval - * The poll interval in milliseconds. - * - * @throws SdkClientException - * If the specified table does not resolve before this method - * times out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * resolve. - */ - public static void waitUntilExists(final DynamoDbClient dynamo, final String tableName, final int timeout, - final int interval) throws InterruptedException { - TableDescription table = waitForTableDescription(dynamo, tableName, null, timeout, interval); - - if (table == null) { - throw SdkClientException.builder().message("Table " + tableName + " never returned a result").build(); - } - } - - /** - * Waits up to 10 minutes for a specified DynamoDB table to move into the - * ACTIVE state. If the table does not exist or does not - * transition to the ACTIVE state after this time, then - * SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table whose status is being checked. - * - * @throws TableNeverTransitionedToStateException - * If the specified table does not exist or does not transition - * into the ACTIVE state before this method times - * out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * transition into the ACTIVE state. - */ - public static void waitUntilActive(final DynamoDbClient dynamo, final String tableName) - throws InterruptedException, TableNeverTransitionedToStateException { - waitUntilActive(dynamo, tableName, DEFAULT_WAIT_TIMEOUT, DEFAULT_WAIT_INTERVAL); - } - - /** - * Waits up to a specified amount of time for a specified DynamoDB table to - * move into the ACTIVE state. If the table does not exist or - * does not transition to the ACTIVE state after this time, - * then a SdkClientException is thrown. - * - * @param dynamo - * The DynamoDB client to use to make requests. - * @param tableName - * The name of the table whose status is being checked. - * @param timeout - * The maximum number of milliseconds to wait. - * @param interval - * The poll interval in milliseconds. - * - * @throws TableNeverTransitionedToStateException - * If the specified table does not exist or does not transition - * into the ACTIVE state before this method times - * out and stops polling. - * @throws InterruptedException - * If the thread is interrupted while waiting for the table to - * transition into the ACTIVE state. - */ - public static void waitUntilActive(final DynamoDbClient dynamo, final String tableName, final int timeout, - final int interval) throws InterruptedException, TableNeverTransitionedToStateException { - TableDescription table = waitForTableDescription(dynamo, tableName, TableStatus.ACTIVE, timeout, interval); - - if (table == null || !table.tableStatus().equals(TableStatus.ACTIVE)) { - throw new TableNeverTransitionedToStateException(tableName, TableStatus.ACTIVE); - } - } - - /** - * Wait for the table to reach the desired status and returns the table - * description - * - * @param dynamo - * Dynamo client to use - * @param tableName - * Table name to poll status of - * @param desiredStatus - * Desired {@link TableStatus} to wait for. If null this method - * simply waits until DescribeTable returns something non-null - * (i.e. any status) - * @param timeout - * Timeout in milliseconds to continue to poll for desired status - * @param interval - * Time to wait in milliseconds between poll attempts - * @return Null if DescribeTables never returns a result, otherwise the - * result of the last poll attempt (which may or may not have the - * desired state) - * @throws {@link - * IllegalArgumentException} If timeout or interval is invalid - */ - private static TableDescription waitForTableDescription(final DynamoDbClient dynamo, final String tableName, - TableStatus desiredStatus, final int timeout, final int interval) - throws InterruptedException, IllegalArgumentException { - if (timeout < 0) { - throw new IllegalArgumentException("Timeout must be >= 0"); - } - if (interval <= 0 || interval >= timeout) { - throw new IllegalArgumentException("Interval must be > 0 and < timeout"); - } - long startTime = System.currentTimeMillis(); - long endTime = startTime + timeout; - - TableDescription table = null; - while (System.currentTimeMillis() < endTime) { - try { - table = dynamo.describeTable(DescribeTableRequest.builder().tableName(tableName).build()).table(); - if (desiredStatus == null || table.tableStatus().equals(desiredStatus)) { - return table; - - } - } catch (ResourceNotFoundException rnfe) { - // ResourceNotFound means the table doesn't exist yet, - // so ignore this error and just keep polling. - } - - Thread.sleep(interval); - } - return table; - } - - /** - * Creates the table and ignores any errors if it already exists. - * @param dynamo The Dynamo client to use. - * @param createTableRequest The create table request. - * @return True if created, false otherwise. - */ - public static boolean createTableIfNotExists(final DynamoDbClient dynamo, final CreateTableRequest createTableRequest) { - try { - dynamo.createTable(createTableRequest); - return true; - } catch (final ResourceInUseException e) { - if (log.isTraceEnabled()) { - log.trace("Table " + createTableRequest.tableName() + " already exists", e); - } - } - return false; - } - - /** - * Deletes the table and ignores any errors if it doesn't exist. - * @param dynamo The Dynamo client to use. - * @param deleteTableRequest The delete table request. - * @return True if deleted, false otherwise. - */ - public static boolean deleteTableIfExists(final DynamoDbClient dynamo, final DeleteTableRequest deleteTableRequest) { - try { - dynamo.deleteTable(deleteTableRequest); - return true; - } catch (final ResourceNotFoundException e) { - if (log.isTraceEnabled()) { - log.trace("Table " + deleteTableRequest.tableName() + " does not exist", e); - } - } - return false; - } - - /** - * Thrown by {@link TableUtils} when a table never reaches a desired state - */ - public static class TableNeverTransitionedToStateException extends SdkClientException { - - private static final long serialVersionUID = 8920567021104846647L; - - public TableNeverTransitionedToStateException(String tableName, TableStatus desiredStatus) { - super(SdkClientException.builder() - .message("Table " + tableName + " never transitioned to desired state of " + - desiredStatus.toString())); - } - - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadIntegrationTest.java deleted file mode 100644 index 166ab59766ef..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadIntegrationTest.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.ConsistentRead; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.mapper.NumberSetAttributeClass; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.pojos.RangeKeyClass; - -public class BatchLoadIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = 1; - private static int startKeyDebug = 1; - DynamoDbMapper mapper = new DynamoDbMapper(dynamo, DynamoDbMapperConfig.builder() - .withSaveBehavior(SaveBehavior.UPDATE) - .withConsistentReads(ConsistentRead.CONSISTENT) - .build()); - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - } - - @AfterClass - public static void tearDown() { - try { - dynamo.deleteTable(DeleteTableRequest.builder().tableName(TABLE_WITH_RANGE_ATTRIBUTE).build()); - } catch (Exception e) { - // Ignore. - } - waitForTableToBecomeDeleted(TABLE_WITH_RANGE_ATTRIBUTE); - } - - @Test - public void testBatchLoad() throws InterruptedException { - // To see whether batchGet can handle more than 100 items per request - final int numItems = 200; - List objs = new ArrayList(); - List keyPairs = new LinkedList(); - Class clazz = null; - for (int i = 0; i < numItems; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - clazz = obj.getClass(); - keyPairs.add(new KeyPair().withHashKey(obj.getKey())); - } - - mapper.batchSave(objs); - - Map, List> itemsToGet = new HashMap, List>(); - Map> response = null; - itemsToGet.put(clazz, keyPairs); - response = mapper.batchLoad(itemsToGet); - List items = response.get(TABLE_NAME); - assertEquals(numItems, items.size()); - - for (Object item : items) { - assertTrue(objs.contains(item)); - } - } - - @Test - public void testMultipleTables() { - final int numItems = 55; - Map, List> itemsToGet = new HashMap, List>(); - Class clazz = null; - List keyPairs = new LinkedList(); - List objs = new ArrayList(); - for (int i = 0; i < numItems * 2; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - clazz = obj.getClass(); - keyPairs.add(new KeyPair().withHashKey(obj.getKey())); - objs.add(obj); - } - itemsToGet.put(clazz, keyPairs); - keyPairs = new LinkedList(); - for (int i = 0; i < numItems; i++) { - RangeKeyClass obj = getUniqueRangeKeyObject(); - clazz = obj.getClass(); - keyPairs.add(new KeyPair().withHashKey(obj.getKey()).withRangeKey(obj.getRangeKey())); - objs.add(obj); - } - itemsToGet.put(clazz, keyPairs); - Collections.shuffle(objs); - - mapper.batchSave(objs); - - Map> response = null; - itemsToGet.put(clazz, keyPairs); - response = mapper.batchLoad(itemsToGet); - - List itemsFromTableOne = response.get(TABLE_NAME); - List itemsFromTableTwo = response.get(TABLE_WITH_RANGE_ATTRIBUTE); - - assertEquals(numItems * 2, itemsFromTableOne.size()); - assertEquals(numItems, itemsFromTableTwo.size()); - - for (Object item : itemsFromTableOne) { - assertTrue(objs.contains(item)); - } - - for (Object item : itemsFromTableTwo) { - assertTrue(objs.contains(item)); - } - } - - @Test - public void testBoudaryCases() { - // The request is an empty Map. - Map, List> itemsToGet = new HashMap, List>(); - Map> response = null; - response = mapper.batchLoad(itemsToGet); - assertTrue(response.isEmpty()); - - // The request only contains invalid key pairs - List keyPairs = new LinkedList(); - Class clazz = getUniqueNumericObject().getClass(); - keyPairs.add(new KeyPair().withHashKey("non-existent-key")); - itemsToGet.clear(); - itemsToGet.put(clazz, keyPairs); - response = mapper.batchLoad(itemsToGet); - assertNotNull(response); - List items = response.get(TABLE_NAME); - assertNotNull(items); - assertEquals(0, items.size()); - - // The request does not contain any key pairs. - itemsToGet.put(clazz, new LinkedList()); - response = mapper.batchLoad(itemsToGet); - assertTrue(response.isEmpty()); - } - - @Test - public void testExponentialBackOffForBatchGetInMapper() - throws NoSuchFieldException, SecurityException, - IllegalArgumentException, IllegalAccessException { - long startTime = System.currentTimeMillis(); - long maxBackOffTimePerRetry = DynamoDbMapper.MAX_BACKOFF_IN_MILLISECONDS; - int NoOfRetries = DynamoDbMapper.BATCH_GET_MAX_RETRY_COUNT_ALL_KEYS; - - List objs = new ArrayList(); - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - DynamoDbClient mockClient = mock(DynamoDbClient.class); - when(mockClient.batchGetItem(any(BatchGetItemRequest.class))).thenAnswer(new Answer() { - @Override - public BatchGetItemResponse answer(InvocationOnMock invocation) throws Throwable { - Thread.sleep(3000); - BatchGetItemResponse result = BatchGetItemResponse.builder() - .responses(new HashMap<>()) - .unprocessedKeys(((BatchGetItemRequest) invocation.getArguments()[0]).requestItems()) - .build(); - return result; - } - }); - DynamoDbMapper mapper = new DynamoDbMapper(mockClient); - try { - mapper.batchLoad(objs); - fail("Expecting an exception due to exceed of number of retries."); - } catch (Exception e) { - e.printStackTrace(); - long endTime = System.currentTimeMillis(); - assertTrue(((endTime - startTime)) > (maxBackOffTimePerRetry - * NoOfRetries)); - } - } - - private NumberSetAttributeClass getUniqueNumericObject() { - NumberSetAttributeClass obj = new NumberSetAttributeClass(); - obj.setKey(String.valueOf(startKeyDebug++)); - obj.setBigDecimalAttribute(toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); - obj.setBigIntegerAttribute( - toSet(new BigInteger("" + startKey++), new BigInteger("" + startKey++), new BigInteger("" + startKey++))); - obj.setByteObjectAttribute(toSet(new Byte(nextByte()), new Byte(nextByte()), new Byte(nextByte()))); - obj.setDoubleObjectAttribute(toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); - obj.setFloatObjectAttribute(toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); - obj.setIntegerAttribute(toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); - obj.setLongObjectAttribute(toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); - obj.setBooleanAttribute(toSet(true, false)); - obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); - Set cals = new HashSet(); - for (Date d : obj.getDateAttribute()) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(d); - cals.add(cal); - } - obj.setCalendarAttribute(toSet(cals)); - return obj; - } - - private RangeKeyClass getUniqueRangeKeyObject() { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(startKey++); - obj.setIntegerAttribute(toSet(start++, start++, start++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setRangeKey(start++); - obj.setStringAttribute("" + startKey++); - obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); - return obj; - } - - private String nextByte() { - return "" + byteStart++ % Byte.MAX_VALUE; - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDBS3IntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDBS3IntegrationTest.java deleted file mode 100644 index c9e448162bf2..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDBS3IntegrationTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import java.io.ByteArrayOutputStream; -import java.util.UUID; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.core.sync.RequestBody; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.pojos.S3LinksTestClass; -import software.amazon.awssdk.services.s3.model.PutObjectRequest; -import software.amazon.awssdk.testutils.RandomTempFile; - -@Ignore -// Revisit S3 -public class DynamoDBS3IntegrationTest extends DynamoDBS3IntegrationTestBase { - - private static final long OBJECT_SIZE = 123; - - @Test - public void testCredentialContext() throws Exception { - tryCreateItem(new DynamoDbMapper(dynamo, CREDENTIALS_PROVIDER_CHAIN)); - } - - @Test - public void testManuallyFilledContext() throws Exception { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo, CREDENTIALS_PROVIDER_CHAIN); - S3ClientCache s3cc = mapper.s3ClientCache(); - s3cc.useClient(s3East, Region.US_EAST_1); - s3cc.useClient(s3West, Region.US_WEST_2); - tryCreateItem(mapper); - } - - public void tryCreateItem(DynamoDbMapper mapper) throws Exception { - String westKey = UUID.randomUUID().toString(); - String eastKey = UUID.randomUUID().toString(); - - S3LinksTestClass obj = new S3LinksTestClass(); - obj.setKey("" + ++startKey); - S3Link linkWest = mapper.createS3Link(Region.US_WEST_2, DynamoDBS3IntegrationTestBase.WEST_BUCKET, westKey); - obj.setS3LinkWest(linkWest); - mapper.save(obj); - obj = mapper.load(S3LinksTestClass.class, obj.getKey()); - - assertObjectDoesntExist(s3West, obj.s3LinkWest().bucketName(), westKey); - - linkWest.getAmazonS3Client().putObject(PutObjectRequest.builder() - .bucket(linkWest.bucketName()) - .key(linkWest.getKey()) - .build(), - RequestBody.fromFile(new RandomTempFile(westKey, OBJECT_SIZE))); - - assertObjectExists(s3West, obj.s3LinkWest().bucketName(), westKey); - - S3Link linkEast = mapper.createS3Link(Region.US_EAST_1, DynamoDBS3IntegrationTestBase.EAST_BUCKET, eastKey); - obj.setS3LinkEast(linkEast); - assertObjectDoesntExist(s3East, obj.s3LinkEast().bucketName(), eastKey); - - linkEast.getAmazonS3Client().putObject(PutObjectRequest.builder() - .bucket(linkEast.bucketName()) - .key(linkEast.getKey()) - .build(), - RequestBody.fromFile(new RandomTempFile(westKey, OBJECT_SIZE))); - mapper.save(obj); - - assertObjectExists(s3West, obj.s3LinkWest().bucketName(), westKey); - assertObjectExists(s3East, obj.s3LinkEast().bucketName(), eastKey); - - obj = mapper.load(S3LinksTestClass.class, obj.getKey()); - - assertEquals(westKey, obj.s3LinkWest().getKey()); - assertEquals(eastKey, obj.s3LinkEast().getKey()); - System.err.println(obj.s3LinkWest().toJson()); - System.err.println(obj.s3LinkEast().toJson()); - mapper.delete(obj); - - // Test the convenience methods on S3Link - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - linkEast.downloadTo(baos); - assertEquals(OBJECT_SIZE, baos.toByteArray().length); - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDBS3IntegrationTestBase.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDBS3IntegrationTestBase.java deleted file mode 100644 index c344783974dd..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDBS3IntegrationTestBase.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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. - */ - -/* - * - * Licensed 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://aws.amazon.com/apache2.0 - * - * This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.fail; -import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName; - -import java.util.Iterator; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.CreateBucketConfiguration; -import software.amazon.awssdk.services.s3.model.CreateBucketRequest; -import software.amazon.awssdk.services.s3.model.DeleteBucketRequest; -import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; -import software.amazon.awssdk.services.s3.model.HeadObjectRequest; -import software.amazon.awssdk.services.s3.model.ListObjectsRequest; -import software.amazon.awssdk.services.s3.model.ListObjectsResponse; -import software.amazon.awssdk.services.s3.model.S3Object; -import utils.test.util.DynamoDBIntegrationTestBase; - -public class DynamoDBS3IntegrationTestBase extends DynamoDBIntegrationTestBase { - public static final String WEST_BUCKET = temporaryBucketName("java-dynamo-s3-integ-test-west"); - public static final String EAST_BUCKET = temporaryBucketName("java-dynamo-s3-integ-test-east"); - - protected static S3Client s3East; - protected static S3Client s3West; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBIntegrationTestBase.setUp(); - s3East = S3Client.builder() - .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) - .region(Region.US_EAST_1) - .build(); - - s3West = S3Client.builder() - .credentialsProvider(CREDENTIALS_PROVIDER_CHAIN) - .region(Region.US_WEST_2) - .build(); - - createBucket(s3East, EAST_BUCKET, null); - createBucket(s3West, WEST_BUCKET, Region.US_WEST_2.id()); - } - - @AfterClass - public static void tearDown() { - deleteBucketAndAllContents(s3East, EAST_BUCKET); - deleteBucketAndAllContents(s3West, WEST_BUCKET); - } - - /** - * Deletes all objects in the specified bucket, and then deletes the bucket. - * - * @param s3 The AmazonS3 client to use. - * @param bucketName The bucket to empty and delete. - */ - protected static void deleteBucketAndAllContents(S3Client s3, String bucketName) { - ListObjectsResponse response = s3.listObjects(ListObjectsRequest.builder() - .bucket(bucketName) - .build()); - - while (true) { - for (Iterator iterator = response.contents().iterator(); iterator.hasNext(); ) { - S3Object objectSummary = (S3Object) iterator.next(); - s3.deleteObject(DeleteObjectRequest.builder() - .bucket(bucketName) - .key(objectSummary.key()) - .build()); - } - - if (response.isTruncated()) { - response = s3.listObjects(ListObjectsRequest.builder() - .marker(response.nextMarker()) - .bucket(bucketName) - .build()); - } else { - break; - } - } - ; - - s3.deleteBucket(DeleteBucketRequest.builder().bucket(bucketName).build()); - } - - /** - * Creates a bucket and waits for it to exist. - * - * @param s3 The AmazonS# client to use. - * @param bucketName The name of the bucket to create. - */ - protected static void createBucket(S3Client s3, String bucketName, String region) throws InterruptedException { - s3.createBucket(CreateBucketRequest.builder() - .bucket(bucketName) - .createBucketConfiguration(CreateBucketConfiguration.builder() - .locationConstraint(region) - .build()) - .build()); - - Thread.sleep(1000); - } - - protected static void maxPollTimeExceeded() { - throw new RuntimeException("Max poll time exceeded"); - } - - /** - * Asserts that the object stored in the specified bucket and key doesn't - * exist If it does exist, this method will fail the current test. - * - * @param s3 The AmazonS3 client to use. - * @param bucketName The name of the bucket containing the object to test. - * @param key The key under which the object is stored in the specified - * bucket. - */ - protected void assertObjectDoesntExist(S3Client s3, String bucketName, String key) throws Exception { - long timeoutTime = System.currentTimeMillis() + 10000; - - while (true) { - try { - s3.headObject(HeadObjectRequest.builder().bucket(bucketName).key(key).build()); - Thread.sleep(1000); - if (System.currentTimeMillis() > timeoutTime) { - fail("object " + bucketName + "/" + key + " still exists"); - } - } catch (SdkServiceException exception) { - /* - * We expect a 404 indicating that the object version we requested - * doesn't exist. If we get anything other than that, then we want - * to let the exception keep going up the chain. - */ - if (exception.statusCode() != 404) { - throw exception; - } - return; // doesn't exist! - } - } - } - - /** - * Asserts that the object stored in the specified bucket and key exists. If - * it doesn't exist, this method will fail the current test. - * - * @param s3 The AmazonS3 client to use. - * @param bucketName The name of the bucket containing the object to test. - * @param key The key under which the object is stored in the specified - * bucket. - */ - protected void assertObjectExists(S3Client s3, String bucketName, String key) throws Exception { - long timeoutTime = System.currentTimeMillis() + 10000; - - while (true) { - try { - s3.headObject(HeadObjectRequest.builder().bucket(bucketName).key(key).build()); - return; // exists! - } catch (SdkServiceException exception) { - /* - * We expect a 404 indicating that the object version we requested - * doesn't exist. If we get anything other than that, then we want - * to let the exception keep going up the chain. - */ - if (exception.statusCode() != 404) { - throw exception; - } - Thread.sleep(1000); - if (System.currentTimeMillis() > timeoutTime) { - fail("object " + bucketName + "/" + key + " doesn't exist"); - } - } - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperExpressionsIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperExpressionsIntegrationTest.java deleted file mode 100644 index 465ddf6a0894..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperExpressionsIntegrationTest.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertTrue; - -import java.io.FileNotFoundException; -import java.io.IOException; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.utils.ImmutableMap.Builder; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; -import software.amazon.awssdk.testutils.Waiter; -import software.amazon.awssdk.testutils.service.AwsTestBase; - -public class DynamoDbMapperExpressionsIntegrationTest extends AwsTestBase { - - /** - * Reference to the mapper used for this testing - */ - protected static DynamoDbMapper mapper; - - /** - * Reference to the client being used by the mapper. - */ - protected static DynamoDbClient client; - - /** - * Table name to be used for this testing - */ - static final String TABLENAME = "java-sdk-mapper-customer"; - - /** - * Attribute name of the hash key - */ - private static final String HASH_KEY = "customerId"; - - /** - * Attribute name of the range key - */ - private static final String RANGE_KEY = "addressType"; - - /** - * Status of the table - */ - private static final String TABLE_STATUS_ACTIVE = "ACTIVE"; - - /** - * Sleep time in milli seconds for the table to become active. - */ - private static final long SLEEP_TIME_IN_MILLIS = 5000; - - /** - * Provisioned Throughput read capacity for the table. - */ - private static final long READ_CAPACITY = 10; - - /** - * Provisioned Throughput write capacity for the table. - */ - private static final long WRITE_CAPACITY = 10; - - private static final String FIRST_CUSTOMER_ID = "1000"; - private static final String ADDRESS_TYPE_HOME = "home"; - private static final String ADDRESS_TYPE_WORK = "work"; - - @BeforeClass - public static void setUp() throws FileNotFoundException, IOException, - InterruptedException { - setUpCredentials(); - client = DynamoDbClient.builder().credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - mapper = new DynamoDbMapper(client); - try { - client.createTable(CreateTableRequest.builder() - .tableName(TABLENAME) - .keySchema(KeySchemaElement.builder().attributeName(HASH_KEY).keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName(RANGE_KEY).keyType(KeyType.RANGE).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(HASH_KEY).attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName(RANGE_KEY).attributeType(ScalarAttributeType.S).build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(READ_CAPACITY) - .writeCapacityUnits(WRITE_CAPACITY) - .build()) - .build()); - } catch (ResourceInUseException ex) { - ex.printStackTrace(); - } - waitForTableCreation(); - fillInData(); - } - - public static void fillInData() { - final Builder record1 = ImmutableMap - .builder(); - record1.put(HASH_KEY, AttributeValue.builder().n(FIRST_CUSTOMER_ID).build()) - .put(RANGE_KEY, AttributeValue.builder().s(ADDRESS_TYPE_WORK).build()) - .put("AddressLine1", - AttributeValue.builder().s("1918 8th Aven").build()) - .put("city", AttributeValue.builder().s("seattle").build()) - .put("state", AttributeValue.builder().s("WA").build()) - .put("zipcode", AttributeValue.builder().n("98104").build()); - final Builder record2 = ImmutableMap - .builder(); - record2.put(HASH_KEY, AttributeValue.builder().n(FIRST_CUSTOMER_ID).build()) - .put(RANGE_KEY, AttributeValue.builder().s(ADDRESS_TYPE_HOME).build()) - .put("AddressLine1", - AttributeValue.builder().s("15606 NE 40th ST").build()) - .put("city", AttributeValue.builder().s("redmond").build()) - .put("state", AttributeValue.builder().s("WA").build()) - .put("zipcode", AttributeValue.builder().n("98052").build()); - - client.putItem(PutItemRequest.builder().tableName(TABLENAME).item(record1.build()).build()); - client.putItem(PutItemRequest.builder().tableName(TABLENAME).item(record2.build()).build()); - } - - public static void waitForTableCreation() throws InterruptedException { - Waiter.run(() -> client.describeTable(r -> r.tableName(TABLENAME))) - .until(r -> r.table().tableStatus() == TableStatus.ACTIVE) - .orFail(); - } - - @AfterClass - public static void tearDown() throws Exception { - try { - if (client != null) { - client.deleteTable(DeleteTableRequest.builder().tableName(TABLENAME).build()); - } - } catch (Exception e) { - // Ignored or expected. - } finally { - if (client != null) { - client.close(); - } - } - } - - /** - * Queries for a record based on hash and range key. Provider a filter - * expression that filters results. - */ - @Test - public void testQueryFilterExpression() { - Customer customer = new Customer(); - customer.setCustomerId(Long.valueOf(FIRST_CUSTOMER_ID)); - - DynamoDbQueryExpression queryExpression = - new DynamoDbQueryExpression() - .withHashKeyValues(customer) - .withRangeKeyCondition(RANGE_KEY, Condition.builder() - .comparisonOperator(ComparisonOperator.EQ) - .attributeValueList(AttributeValue.builder().s(ADDRESS_TYPE_HOME).build()) - .build()); - PaginatedQueryList results = mapper.query(Customer.class, - queryExpression); - assertTrue(results.size() == 1); - - final Builder builder = ImmutableMap - .builder(); - builder.put(":zipcode", AttributeValue.builder().n("98109").build()); - - queryExpression = queryExpression - .withFilterExpression("zipcode = :zipcode") - .withExpressionAttributeValues(builder.build()); - results = mapper.query(Customer.class, queryExpression); - assertTrue(results.size() == 0); - } - - /** - * Queries using key condition expression. - */ - @Test - public void testKeyConditionExpression() { - Customer customer = new Customer(); - customer.setCustomerId(Long.valueOf(FIRST_CUSTOMER_ID)); - - DynamoDbQueryExpression query = - new DynamoDbQueryExpression() - .withKeyConditionExpression( - "customerId = :customerId AND addressType = :addressType"); - final Builder builder = - ImmutableMap.builder(); - builder.put(":customerId", AttributeValue.builder().n(FIRST_CUSTOMER_ID).build()) - .put(":addressType", AttributeValue.builder().s(ADDRESS_TYPE_HOME).build()) - ; - query.withExpressionAttributeValues(builder.build()); - - PaginatedQueryList results = mapper.query(Customer.class, query); - assertTrue(results.size() == 1); - - builder.put(":zipcode", AttributeValue.builder().n("98109").build()); - query.withFilterExpression("zipcode = :zipcode") - .withExpressionAttributeValues(builder.build()); - - results = mapper.query(Customer.class, query); - assertTrue(results.size() == 0); - } - - /** - * Scan the table and filters the results based on the filter expression - * provided. - */ - @Test - public void testScanFilterExpression() { - Customer customer = new Customer(); - customer.setCustomerId(Long.valueOf(FIRST_CUSTOMER_ID)); - - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression(); - - PaginatedScanList results = mapper.scan(Customer.class, - scanExpression); - assertTrue(results.size() == 2); - - final Builder attributeValueMapBuilder = ImmutableMap - .builder(); - attributeValueMapBuilder - .put(":state", AttributeValue.builder().s("WA").build()); - - final Builder attributeNameMapBuilder = ImmutableMap - .builder(); - attributeNameMapBuilder.put("#statename", "state"); - - scanExpression = scanExpression - .withFilterExpression("#statename = :state") - .withExpressionAttributeValues(attributeValueMapBuilder.build()) - .withExpressionAttributeNames(attributeNameMapBuilder.build()); - results = mapper.scan(Customer.class, scanExpression); - assertTrue(results.size() == 2); - } - - /** - * Performs delete operation with a condition expression specified. Delete - * should fail as the condition in the conditional expression evaluates to - * false. - */ - @Test - public void testDeleteConditionalExpression() { - Customer customer = new Customer(); - customer.setCustomerId(Long.valueOf(FIRST_CUSTOMER_ID)); - customer.setAddressType(ADDRESS_TYPE_WORK); - - Builder expectedMapBuilder = ImmutableMap - .builder(); - expectedMapBuilder.put("zipcode", ExpectedAttributeValue.builder() - .attributeValueList(AttributeValue.builder().n("98052").build()) - .comparisonOperator(ComparisonOperator.EQ).build()); - - DynamoDbDeleteExpression deleteExpression = new DynamoDbDeleteExpression(); - deleteExpression.setConditionExpression("zipcode = :zipcode"); - - final Builder attributeValueMapBuilder = ImmutableMap - .builder(); - attributeValueMapBuilder.put(":zipcode", - AttributeValue.builder().n("98052").build()); - deleteExpression.setExpressionAttributeValues(attributeValueMapBuilder - .build()); - try { - mapper.delete(customer, deleteExpression); - } catch (Exception e) { - assertTrue(e instanceof ConditionalCheckFailedException); - } - } - - // Note don't move Customer to top level, or else it would break the release - // pipeline, as the integration test will not be copied over causing - // compilation failure - @DynamoDbTable(tableName = DynamoDbMapperExpressionsIntegrationTest.TABLENAME) - public static class Customer { - - private long customerId; - - private String addressType; - - private String addressLine1; - - private String city; - - private String state; - - private int zipcode; - - @DynamoDbAttribute(attributeName = "customerId") - @DynamoDbHashKey(attributeName = "customerId") - public long getCustomerId() { - return customerId; - } - - public void setCustomerId(long customerId) { - this.customerId = customerId; - } - - @DynamoDbAttribute(attributeName = "addressType") - @DynamoDbRangeKey(attributeName = "addressType") - public String getAddressType() { - return addressType; - } - - public void setAddressType(String addressType) { - this.addressType = addressType; - } - - @DynamoDbAttribute(attributeName = "AddressLine1") - public String getAddressLine1() { - return addressLine1; - } - - public void setAddressLine1(String addressLine1) { - this.addressLine1 = addressLine1; - } - - @DynamoDbAttribute(attributeName = "city") - public String getCity() { - return city; - } - - public void setCity(String city) { - this.city = city; - } - - @DynamoDbAttribute(attributeName = "state") - public String state() { - return state; - } - - public void setState(String state) { - this.state = state; - } - - @DynamoDbAttribute(attributeName = "zipcode") - public int getZipcode() { - return zipcode; - } - - public void setZipcode(int zipcode) { - this.zipcode = zipcode; - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/EnumMarshallerIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/EnumMarshallerIntegrationTest.java deleted file mode 100644 index 4a27f4184592..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/EnumMarshallerIntegrationTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; - -/** - * Status tests for {@code EnumMarshaller}. - */ -public class EnumMarshallerIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - @Test - public void testNullEnumValue() { - final DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - final TestObject object1 = new TestObject(); - - assertNull(object1.getStatus()); - - mapper.save(object1); - - final TestObject object2 = mapper.load(TestObject.class, object1.getKey()); - - assertNull(object2.getStatus()); - } - - @Test - public void testMarshalling() { - final DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - final TestObject object1 = new TestObject(); - - object1.setStatus(TestObject.Status.Y); - - mapper.save(object1); - - assertNotNull(object1.getKey()); - assertNotNull(object1.getStatus()); - - final TestObject object2 = mapper.load(TestObject.class, object1.getKey()); - - assertEquals(object1.getKey(), object2.getKey()); - assertEquals(object1.getStatus(), object2.getStatus()); - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class TestObject { - private String key; - - - private Status status; - - - - @DynamoDbHashKey - @DynamoDbAutoGeneratedKey - public String getKey() { - return this.key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbMarshalling(marshallerClass = StatusEnumMarshaller.class) - public Status getStatus() { - return this.status; - } - - public void setStatus(Status status) { - this.status = status; - } - - public static enum Status { - X, - Y, - Z - } - - public static class StatusEnumMarshaller extends AbstractEnumMarshaller { - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonIntegrationTest.java deleted file mode 100644 index 2f9b1db9f33f..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonIntegrationTest.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.TableNameOverride; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; -import software.amazon.awssdk.testutils.Waiter; -import software.amazon.awssdk.testutils.service.AwsTestBase; - -public class JsonIntegrationTest extends AwsTestBase { - - private static final String TABLE_NAME = "test-table-" - + UUID.randomUUID().toString(); - - private static DynamoDbClient client; - private static DynamoDbMapper mapper; - - @BeforeClass - public static void setup() throws Exception { - setUpCredentials(); - client = DynamoDbClient.builder().credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).region(Region.US_WEST_2).build(); - - mapper = new DynamoDbMapper( - client, - new DynamoDbMapperConfig.Builder() - .withConversionSchema(ConversionSchemas.V2) - .withTableNameOverride(TableNameOverride - .withTableNameReplacement(TABLE_NAME)) - .withConsistentReads(DynamoDbMapperConfig.ConsistentRead.CONSISTENT) - .build()); - - CreateTableRequest request = mapper - .generateCreateTableRequest(TestClass.class).toBuilder() - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(1L).writeCapacityUnits(1L).build()) - .build(); - - client.createTable(request); - - Waiter.run(() -> client.describeTable(r -> r.tableName(TABLE_NAME))) - .until(r -> r.table().tableStatus() == TableStatus.ACTIVE) - .orFail(); - } - - @AfterClass - public static void cleanup() { - if (client == null) { - return; - } - - try { - client.deleteTable(DeleteTableRequest.builder().tableName(TABLE_NAME).build()); - } catch (ResourceNotFoundException e) { - // Ignored or expected. - } - } - - private static boolean eq(T one, T two) { - if (one == null) { - return (two == null); - } else { - return one.equals(two); - } - } - - @Test - @Ignore - public void testIt() { - final ChildClass child1 = new ChildClass(); - child1.setBool(true); - - final ChildClass child2 = new ChildClass(); - child2.setBool(true); - - final ChildClass parent = new ChildClass(); - parent.setFirstChild(child1); - parent.setOtherChildren(Arrays.asList(child1, child2)); - parent.setNamedChildren(new HashMap() {{ - put("one", child1); - put("two", child2); - }}); - - TestClass test = new TestClass(); - test.setId("test"); - test.setListOfMaps(Arrays.>asList( - new HashMap() {{ - put("parent", parent); - }}, - new HashMap() {{ - put("parent", parent); - }}, - null - )); - test.setMapOfLists(new HashMap>() {{ - put("parent", Arrays.asList(child1, child2)); - put("child2", Collections.emptyList()); - put("child1", null); - }}); - - mapper.save(test); - - TestClass result = mapper.load(TestClass.class, "test"); - - Assert.assertEquals(test, result); - } - - @DynamoDbTable(tableName = "") - public static class TestClass { - - private String id; - private List> listOfMaps; - private Map> mapOfLists; - - @DynamoDbHashKey - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public List> getListOfMaps() { - return listOfMaps; - } - - public void setListOfMaps(List> listOfMaps) { - this.listOfMaps = listOfMaps; - } - - public Map> getMapOfLists() { - return mapOfLists; - } - - public void setMapOfLists(Map> mapOfLists) { - this.mapOfLists = mapOfLists; - } - - @Override - public boolean equals(Object obj) { - TestClass other = (TestClass) obj; - - return (eq(id, other.id) - && eq(listOfMaps, other.listOfMaps) - && eq(mapOfLists, other.mapOfLists)); - } - - @Override - public String toString() { - return "{id=" + id + ", listOfMaps=" + listOfMaps + ", mapOfLists=" - + mapOfLists + "}"; - } - } - - @DynamoDbDocument - public static class ChildClass { - - private boolean bool; - - private ChildClass firstChild; - private List otherChildren; - private Map namedChildren; - - public boolean isBool() { - return bool; - } - - public void setBool(boolean bool) { - this.bool = bool; - } - - public ChildClass getFirstChild() { - return firstChild; - } - - public void setFirstChild(ChildClass firstChild) { - this.firstChild = firstChild; - } - - public List getOtherChildren() { - return otherChildren; - } - - public void setOtherChildren(List otherChildren) { - this.otherChildren = otherChildren; - } - - public Map getNamedChildren() { - return namedChildren; - } - - public void setNamedChildren(Map namedChildren) { - this.namedChildren = namedChildren; - } - - @Override - public boolean equals(Object obj) { - ChildClass other = (ChildClass) obj; - - return (eq(bool, other.bool) - && eq(firstChild, other.firstChild) - && eq(otherChildren, other.otherChildren) - && eq(namedChildren, other.namedChildren)); - } - - @Override - public String toString() { - return "{bool=" + bool + ", firstChild=" + firstChild - + ", otherChildren=" + otherChildren + ", namedChildren=" - + namedChildren + "}"; - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonMarshallerIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonMarshallerIntegrationTest.java deleted file mode 100644 index 29863e4130a6..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonMarshallerIntegrationTest.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; - -/** - * Status tests for {@code JsonMarshaller}. - */ -public class JsonMarshallerIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - @Test - public void testMarshalling() { - final DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - final TestObject object1 = new TestObject(); - object1.setOneItem(new TestObject.OneItem()); - object1.addOneItem(new TestObject.OneItem(UUID.randomUUID().toString(), 1)); - object1.addOneItem(new TestObject.OneItem(UUID.randomUUID().toString(), 2)); - object1.addOneItem(new TestObject.OneItem(UUID.randomUUID().toString(), 3)); - object1.setTwoItem(new TestObject.TwoItem()); - object1.addTwoItem(new TestObject.TwoItem(UUID.randomUUID().toString(), new Date())); - - mapper.save(object1); - - assertNotNull(object1.getKey()); - - assertNotNull(object1.getOneItem()); - assertNotNull(object1.getOneItems()); - assertEquals(3, object1.getOneItems().size()); - - assertNotNull(object1.getTwoItem()); - assertNotNull(object1.getTwoItems()); - assertEquals(1, object1.getTwoItems().size()); - - final TestObject object2 = mapper.load(TestObject.class, object1.getKey()); - - assertEquals(object1.getKey(), object2.getKey()); - - assertEquals(object1.getOneItem().getId(), object2.getOneItem().getId()); - assertEquals(object1.getOneItem().getQuantity(), object2.getOneItem().getQuantity()); - assertEquals(object1.getOneItems().size(), object2.getOneItems().size()); - - for (int i = 0, its = object1.getOneItems().size(); i < its; i++) { - assertEquals(object1.getOneItems().get(i).getId(), object2.getOneItems().get(i).getId()); - assertEquals(object1.getOneItems().get(i).getQuantity(), object2.getOneItems().get(i).getQuantity()); - } - - assertEquals(object1.getTwoItem().getId(), object2.getTwoItem().getId()); - assertEquals(object1.getTwoItem().getDate(), object2.getTwoItem().getDate()); - assertEquals(object1.getTwoItems().size(), object2.getTwoItems().size()); - - for (int i = 0, its = object1.getTwoItems().size(); i < its; i++) { - assertEquals(object1.getTwoItems().get(i).getId(), object2.getTwoItems().get(i).getId()); - assertEquals(object1.getTwoItems().get(i).getDate(), object2.getTwoItems().get(i).getDate()); - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class TestObject { - private String key; - - - private OneItem aitem; - - - private List oneItems; - - - private TwoItem bitem; - - - private List twoItems; - - @DynamoDbHashKey - @DynamoDbAutoGeneratedKey - public String getKey() { - return this.key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbMarshalling(marshallerClass = OneItemJsonMarshaller.class) - public OneItem getOneItem() { - return this.aitem; - } - - public void setOneItem(OneItem aitem) { - this.aitem = aitem; - } - - @DynamoDbMarshalling(marshallerClass = OneListJsonMarshaller.class) - public List getOneItems() { - return this.oneItems; - } - - public void setOneItems(List oneItems) { - this.oneItems = oneItems; - } - - public void addOneItem(OneItem aitem) { - if (this.oneItems == null) { - this.oneItems = new ArrayList(); - } - this.oneItems.add(aitem); - } - - @DynamoDbMarshalling(marshallerClass = TwoItemJsonMarshaller.class) - public TwoItem getTwoItem() { - return this.bitem; - } - - public void setTwoItem(TwoItem bitem) { - this.bitem = bitem; - } - - @DynamoDbMarshalling(marshallerClass = TwoListJsonMarshaller.class) - public List getTwoItems() { - return this.twoItems; - } - - public void setTwoItems(List twoItems) { - this.twoItems = twoItems; - } - - public void addTwoItem(TwoItem bitem) { - if (this.twoItems == null) { - this.twoItems = new ArrayList(); - } - this.twoItems.add(bitem); - } - - public static class OneItemJsonMarshaller extends JsonMarshaller { - } - - public static class OneListJsonMarshaller extends JsonMarshaller { - public OneListJsonMarshaller() { - super(Type.class); - } - - ; - - public static final class Type extends ArrayList { - } - } - - public static class TwoItemJsonMarshaller extends JsonMarshaller { - } - - public static class TwoListJsonMarshaller extends JsonMarshaller { - public TwoListJsonMarshaller() { - super(Type.class); - } - - ; - - public static final class Type extends ArrayList { - } - } - - public static class OneItem { - private String id; - private Integer quantity; - - public OneItem(String id, Integer quantity) { - this.id = id; - this.quantity = quantity; - } - - public OneItem() { - this(null, null); - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - public Integer getQuantity() { - return this.quantity; - } - - public void setQuantity(Integer quantity) { - this.quantity = quantity; - } - } - - public static class TwoItem { - private String id; - private Date date; - - public TwoItem(String id, Date date) { - this.id = id; - this.date = date; - } - - public TwoItem() { - this(null, null); - } - - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - public Date getDate() { - return this.date; - } - - public void setDate(Date date) { - this.date = date; - } - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3ClientCacheIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3ClientCacheIntegrationTest.java deleted file mode 100644 index d20d9cd134fb..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3ClientCacheIntegrationTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.net.URI; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; - -@Ignore -// FIXME: Depends on S3 properly parsing region information from the endpoint (see AmazonS3#getRegionName()) -public class S3ClientCacheIntegrationTest { - private AwsBasicCredentials credentials; - - @Before - public void setUp() { - credentials = AwsBasicCredentials.create("mock", "mock"); - } - - @Test - public void testBadClientCache() throws Exception { - S3ClientCache s3cc = new S3ClientCache(credentials); - S3Client notAnAWSEndpoint = S3Client.builder() - .credentialsProvider(StaticCredentialsProvider.create(credentials)) - .endpointOverride(new URI("i.am.an.invalid.aws.endpoint.com")) - .build(); - - try { - s3cc.useClient(notAnAWSEndpoint, Region.US_EAST_2); - } catch (IllegalStateException e) { - assertTrue(e.getMessage().contains("No valid region has been specified. Unable to return region name")); - return; - } - - fail("Expected exception to be thrown"); - } - - @Test - public void testNonExistantRegion() throws Exception { - S3ClientCache s3cc = new S3ClientCache(credentials); - S3Client notAnAWSEndpoint = S3Client.builder() - .credentialsProvider(StaticCredentialsProvider.create(credentials)) - .endpointOverride(new URI("s3.mordor.amazonaws.com")) - .build(); - - try { - s3cc.useClient(notAnAWSEndpoint, Region.US_EAST_2); - } catch (IllegalStateException e) { - assertEquals("No valid region has been specified. Unable to return region name", e.getMessage()); - return; - } - - fail("Expected IllegalStateException to be thrown"); - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/document/QueryIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/document/QueryIntegrationTest.java deleted file mode 100644 index def8a2fcdd40..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/document/QueryIntegrationTest.java +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.document; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Random; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.pojos.RangeKeyClass; - -/** - * Integration tests for the query operation on DynamoDBMapper. - */ -public class QueryIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final long HASH_KEY = System.currentTimeMillis(); - private static final int TEST_ITEM_NUMBER = 500; - private static RangeKeyClass hashKeyObject; - private static DynamoDbMapper mapper; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - - DynamoDbMapperConfig mapperConfig = new DynamoDbMapperConfig(DynamoDbMapperConfig.ConsistentRead.CONSISTENT); - mapper = new DynamoDbMapper(dynamo, mapperConfig); - - putTestData(mapper, TEST_ITEM_NUMBER); - - hashKeyObject = new RangeKeyClass(); - hashKeyObject.setKey(HASH_KEY); - } - - /** - * Use BatchSave to put some test data into the tested table. Each item is - * hash-keyed by the same value, and range-keyed by numbers starting from 0. - */ - private static void putTestData(DynamoDbMapper mapper, int itemNumber) { - List objs = new ArrayList(); - for (int i = 0; i < itemNumber; i++) { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(HASH_KEY); - obj.setRangeKey(i); - obj.setBigDecimalAttribute(new BigDecimal(i)); - objs.add(obj); - } - mapper.batchSave(objs); - } - - @Test - public void testQueryWithPrimaryRangeKey() throws Exception { - DynamoDbQueryExpression queryExpression = - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyObject) - .withRangeKeyCondition( - "rangeKey", - Condition.builder() - .comparisonOperator(ComparisonOperator.GT) - .attributeValueList(AttributeValue.builder().n("1.0").build()) - .build()) - .withLimit(11); - List list = mapper.query(RangeKeyClass.class, queryExpression); - - int count = 0; - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - count++; - RangeKeyClass next = iterator.next(); - assertTrue(next.getRangeKey() > 1.00); - } - - int numMatchingObjects = TEST_ITEM_NUMBER - 2; - assertEquals(count, numMatchingObjects); - assertEquals(numMatchingObjects, list.size()); - - assertNotNull(list.get(list.size() / 2)); - assertTrue(list.contains(list.get(list.size() / 2))); - assertEquals(numMatchingObjects, list.toArray().length); - - Thread.sleep(250); - int totalCount = mapper.count(RangeKeyClass.class, queryExpression); - assertEquals(numMatchingObjects, totalCount); - - /** - * Tests query with only hash key - */ - queryExpression = new DynamoDbQueryExpression().withHashKeyValues(hashKeyObject); - list = mapper.query(RangeKeyClass.class, queryExpression); - assertEquals(TEST_ITEM_NUMBER, list.size()); - } - - /** - * Tests making queries using query filter on non-key attributes. - */ - @Test - public void testQueryFilter() { - // A random filter condition to be applied to the query. - Random random = new Random(); - int randomFilterValue = random.nextInt(TEST_ITEM_NUMBER); - Condition filterCondition = Condition.builder() - .comparisonOperator(ComparisonOperator.LT) - .attributeValueList( - AttributeValue.builder().n(Integer.toString(randomFilterValue)).build()).build(); - - /* - * (1) Apply the filter on the range key, in form of key condition - */ - DynamoDbQueryExpression queryWithRangeKeyCondition = - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyObject) - .withRangeKeyCondition("rangeKey", filterCondition); - List rangeKeyConditionResult = mapper.query(RangeKeyClass.class, queryWithRangeKeyCondition); - - /* - * (2) Apply the filter on the bigDecimalAttribute, in form of query filter - */ - DynamoDbQueryExpression queryWithQueryFilterCondition = - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyObject) - .withQueryFilter(Collections.singletonMap("bigDecimalAttribute", filterCondition)); - List queryFilterResult = mapper.query(RangeKeyClass.class, queryWithQueryFilterCondition); - - assertEquals(rangeKeyConditionResult.size(), queryFilterResult.size()); - for (int i = 0; i < rangeKeyConditionResult.size(); i++) { - assertEquals(rangeKeyConditionResult.get(i), queryFilterResult.get(i)); - } - } - - /** - * Tests that exception should be raised when user provides an index name - * when making query with the primary range key. - */ - @Test - public void testUnnecessaryIndexNameException() { - try { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - long hashKey = System.currentTimeMillis(); - RangeKeyClass keyObject = new RangeKeyClass(); - keyObject.setKey(hashKey); - DynamoDbQueryExpression queryExpression = new DynamoDbQueryExpression() - .withHashKeyValues(keyObject); - queryExpression.withRangeKeyCondition("rangeKey", - Condition.builder().comparisonOperator(ComparisonOperator.GT.toString()) - .attributeValueList( - AttributeValue.builder().n("1.0").build()).build()).withLimit(11) - .withIndexName("some_index"); - mapper.query(RangeKeyClass.class, queryExpression); - fail("User should not provide index name when making query with the primary range key"); - } catch (IllegalArgumentException expected) { - System.out.println(expected.getMessage()); - } catch (Exception e) { - fail("Should trigger SdkClientException."); - } - - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AbstractKeyAndValIntegrationTestCase.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AbstractKeyAndValIntegrationTestCase.java deleted file mode 100644 index 3b9f495f3b73..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AbstractKeyAndValIntegrationTestCase.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.List; -import org.junit.After; -import org.junit.Before; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.pojos.KeyAndVal; - -/** - * Tests updating component attribute fields correctly. - */ -public abstract class AbstractKeyAndValIntegrationTestCase extends DynamoDBMapperIntegrationTestBase { - - /** - * The DynamoDBMapper instance. - */ - protected DynamoDbMapper util; - - /** - * Sets up the test case. - */ - protected final void setUpTest(final DynamoDbMapperConfig.SaveBehavior saveBehavior) { - this.util = new DynamoDbMapper(dynamo, new DynamoDbMapperConfig.Builder().withSaveBehavior(saveBehavior).build()); - } - - /** - * Sets up the test case. - */ - @Before - public void setUpTest() { - setUpTest(DynamoDbMapperConfig.DEFAULT.saveBehavior()); - } - - /** - * Tears down the test case. - */ - @After - public void tearDownTest() { - this.util = null; - } - - /** - * Assert that the object updated appropriately. - * - * @param changeExpected True if a change is expected. - * @param objects The objects. - */ - protected final void assertBeforeAndAfterChange(final boolean changeExpected, - final List> objects) { - final List befores = new ArrayList(objects.size()); - for (final KeyAndVal object : objects) { - befores.add(object.getVal()); - } - this.util.batchSave(objects); - for (int i = 0, its = objects.size(); i < its; i++) { - assertBeforeAndAfterChange(changeExpected, befores.get(i), objects.get(i).getVal()); - } - } - - /** - * Assert that the object updated appropriately. - * - * @param changeExpected True if a change is expected. - * @param object The object. - * @return The value if more assertions are required. - */ - protected final V assertBeforeAndAfterChange(final Boolean changeExpected, final KeyAndVal object) { - final V before = object.getVal(); - this.util.save(object); - final V after = object.getVal(); - if (changeExpected != null) { - assertBeforeAndAfterChange(changeExpected, before, after); - } - final KeyAndVal reload = this.util.load(object.getClass(), object.getKey()); - assertNotNull(reload); - if (changeExpected != null) { - assertBeforeAndAfterChange(false, after, reload.getVal()); - assertBeforeAndAfterChange(changeExpected, before, reload.getVal()); - } - return reload.getVal(); - } - - /** - * Assert that the object updated appropriately. - * - * @param changeExpected True if a change is expected. - * @param before The before value. - * @param after The after value. - */ - protected final void assertBeforeAndAfterChange(final boolean changeExpected, final V before, final V after) { - if (!changeExpected) { - assertEquals(String.format("Expected before[%s] and after[%s] to be equal", before, after), before, after); - } else if (before == null) { - assertNotNull(String.format("Expected after[%s] to not be null", after), after); - } else { - assertFalse(String.format("Expected before[%s] and after[%s] to not be equal", before, after), before.equals(after)); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AutoGeneratedKeysIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AutoGeneratedKeysIntegrationTest.java deleted file mode 100644 index f582c9d1d3f3..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AutoGeneratedKeysIntegrationTest.java +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.util.Collections; -import java.util.UUID; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbSaveExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.Projection; -import software.amazon.awssdk.services.dynamodb.model.ProjectionType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -/** - * Tests using auto-generated keys for range keys, hash keys, or both. - */ -public class AutoGeneratedKeysIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String TABLE_NAME = "aws-java-sdk-string-range"; - - private static final String GSI_NAME = "gsi-with-autogenerated-keys"; - private static final String GSI_HASH_KEY = "gis-hash-key"; - private static final String GSI_RANGE_KEY = "gis-range-key"; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperIntegrationTestBase.setUp(); - - String keyName = DynamoDBMapperIntegrationTestBase.KEY_NAME; - String rangeKeyAttributeName = "rangeKey"; - - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_NAME) - .keySchema( - KeySchemaElement.builder().attributeName(keyName).keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName(rangeKeyAttributeName).keyType(KeyType.RANGE).build()) - .globalSecondaryIndexes(GlobalSecondaryIndex.builder() - .indexName(GSI_NAME) - .keySchema( - KeySchemaElement.builder().attributeName(GSI_HASH_KEY).keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName(GSI_RANGE_KEY).keyType(KeyType.RANGE).build()) - .projection(Projection.builder().projectionType(ProjectionType.ALL).build()) - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(3L).writeCapacityUnits(3L).build()).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(keyName).attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName(rangeKeyAttributeName).attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName(GSI_HASH_KEY).attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName(GSI_RANGE_KEY).attributeType(ScalarAttributeType.S).build()) - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(5L).build()).build(); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - } - } - - @Test - public void testHashKeyRangeKeyBothAutogenerated() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); - obj.setOtherAttribute("blah"); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyRangeKeyBothAutoGenerated other = mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), - obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testHashKeyRangeKeyBothAutogeneratedBatchWrite() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); - obj.setOtherAttribute("blah"); - HashKeyRangeKeyBothAutoGenerated obj2 = new HashKeyRangeKeyBothAutoGenerated(); - obj2.setOtherAttribute("blah"); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - assertNull(obj2.getKey()); - assertNull(obj2.getRangeKey()); - mapper.batchSave(obj, obj2); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - assertNotNull(obj2.getKey()); - assertNotNull(obj2.getRangeKey()); - - assertEquals(mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), - obj.getRangeKey()), obj); - assertEquals(mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj2.getKey(), - obj2.getRangeKey()), obj2); - } - - /** - * Tests providing additional expected conditions when saving item with - * auto-generated keys. - */ - @Test - public void testAutogeneratedKeyWithUserProvidedExpectedConditions() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - HashKeyRangeKeyBothAutoGenerated obj = new HashKeyRangeKeyBothAutoGenerated(); - obj.setOtherAttribute("blah"); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - - // Add additional expected conditions via DynamoDBSaveExpression. - // Expected conditions joined by AND are compatible with the conditions - // for auto-generated keys. - DynamoDbSaveExpression saveExpression = new DynamoDbSaveExpression(); - saveExpression - .withExpected(Collections.singletonMap( - "otherAttribute", ExpectedAttributeValue.builder().exists(false).build())) - .withConditionalOperator(ConditionalOperator.AND); - // The save should succeed since the user provided conditions are joined by AND. - mapper.save(obj, saveExpression); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyRangeKeyBothAutoGenerated other = mapper.load(HashKeyRangeKeyBothAutoGenerated.class, obj.getKey(), - obj.getRangeKey()); - assertEquals(other, obj); - - // Change the conditional operator to OR. - // IllegalArgumentException is expected since the additional expected - // conditions cannot be joined with the conditions for auto-generated - // keys. - saveExpression.setConditionalOperator(ConditionalOperator.OR); - try { - mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); - } catch (IllegalArgumentException expected) { - // Expected. - } - - // User-provided OR conditions should work if they completely override the generated conditions. - saveExpression - .withExpected(ImmutableMap.of( - "otherAttribute", ExpectedAttributeValue.builder().exists(false).build(), - "key", ExpectedAttributeValue.builder().exists(false).build(), - "rangeKey", ExpectedAttributeValue.builder().exists(false).build())) - .withConditionalOperator(ConditionalOperator.OR); - mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); - - saveExpression - .withExpected(ImmutableMap.of( - "otherAttribute", ExpectedAttributeValue.builder().value(AttributeValue.builder().s("non-existent-value").build()).build(), - "key", ExpectedAttributeValue.builder().value(AttributeValue.builder().s("non-existent-value").build()).build(), - "rangeKey", ExpectedAttributeValue.builder().value(AttributeValue.builder().s("non-existent-value").build()).build())) - .withConditionalOperator(ConditionalOperator.OR); - try { - mapper.save(new HashKeyRangeKeyBothAutoGenerated(), saveExpression); - } catch (ConditionalCheckFailedException expected) { - // Expected. - } - } - - @Test - public void testHashKeyAutogenerated() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - HashKeyAutoGenerated obj = new HashKeyAutoGenerated(); - obj.setOtherAttribute("blah"); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyAutoGenerated other = mapper.load(HashKeyAutoGenerated.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testRangeKeyAutogenerated() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - RangeKeyAutoGenerated obj = new RangeKeyAutoGenerated(); - obj.setOtherAttribute("blah"); - obj.setKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - RangeKeyAutoGenerated other = mapper.load(RangeKeyAutoGenerated.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testNothingAutogenerated() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - NothingAutoGenerated obj = new NothingAutoGenerated(); - obj.setOtherAttribute("blah"); - obj.setKey("" + System.currentTimeMillis()); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - NothingAutoGenerated other = mapper.load(NothingAutoGenerated.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testNothingAutogeneratedErrors() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - NothingAutoGenerated obj = new NothingAutoGenerated(); - - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setRangeKey("" + System.currentTimeMillis()); - obj.setKey(null); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setRangeKey(""); - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setRangeKey("" + System.currentTimeMillis()); - mapper.save(obj); - } - - @Test - public void testHashKeyRangeKeyBothAutogeneratedKeyOnly() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - HashKeyRangeKeyBothAutoGeneratedKeyOnly obj = new HashKeyRangeKeyBothAutoGeneratedKeyOnly(); - - assertNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyRangeKeyBothAutoGeneratedKeyOnly other = mapper.load(HashKeyRangeKeyBothAutoGeneratedKeyOnly.class, obj.getKey(), - obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testHashKeyAutogeneratedKeyOnly() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - HashKeyAutoGeneratedKeyOnly obj = new HashKeyAutoGeneratedKeyOnly(); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - HashKeyAutoGeneratedKeyOnly other = mapper.load(HashKeyAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testRangeKeyAutogeneratedKeyOnly() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - RangeKeyAutoGeneratedKeyOnly obj = new RangeKeyAutoGeneratedKeyOnly(); - obj.setKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - RangeKeyAutoGeneratedKeyOnly other = mapper.load(RangeKeyAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testNothingAutogeneratedKeyOnly() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - NothingAutoGeneratedKeyOnly obj = new NothingAutoGeneratedKeyOnly(); - obj.setKey("" + System.currentTimeMillis()); - obj.setRangeKey("" + System.currentTimeMillis()); - - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - mapper.save(obj); - assertNotNull(obj.getKey()); - assertNotNull(obj.getRangeKey()); - - NothingAutoGeneratedKeyOnly other = mapper.load(NothingAutoGeneratedKeyOnly.class, obj.getKey(), obj.getRangeKey()); - assertEquals(other, obj); - } - - @Test - public void testNothingAutogeneratedKeyOnlyErrors() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - NothingAutoGeneratedKeyOnly obj = new NothingAutoGeneratedKeyOnly(); - - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setRangeKey("" + System.currentTimeMillis()); - obj.setKey(null); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setRangeKey(""); - obj.setKey("" + System.currentTimeMillis()); - try { - mapper.save(obj); - fail("Expected a mapping exception"); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setRangeKey("" + System.currentTimeMillis()); - mapper.save(obj); - } - - @Test - public void testIndexKeyWithAutogeneratedAnnotation_StillRequirePrimaryKeyValue() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - PrimaryKeysNotAutogeneratedIndexKeysAutogenerated obj = new PrimaryKeysNotAutogeneratedIndexKeysAutogenerated(); - - try { - mapper.save(obj); - fail("DynamoDBMappingException is expected."); - } catch (DynamoDbMappingException expected) { - // Expected. - } - - obj.setGsiHashKey("foo"); - obj.setGsiRangeKey("foo"); - try { - mapper.save(obj); - fail("DynamoDBMappingException is expected."); - } catch (DynamoDbMappingException expected) { - // Expected. - } - } - - @Test - public void testIndexKeyWithAutogeneratedAnnotation_AutogenerateIndexKeyValueIfNull() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - PrimaryKeysNotAutogeneratedIndexKeysAutogenerated obj = new PrimaryKeysNotAutogeneratedIndexKeysAutogenerated(); - - String randomPrimaryKeyValue = UUID.randomUUID().toString(); - obj.setKey(randomPrimaryKeyValue); - obj.setRangeKey(randomPrimaryKeyValue); - - assertNull(obj.getGsiHashKey()); - assertNull(obj.getGsiRangeKey()); - mapper.save(obj); - - // check in-memory value - assertNotNull(obj.getGsiHashKey()); - assertNotNull(obj.getGsiRangeKey()); - - PrimaryKeysNotAutogeneratedIndexKeysAutogenerated retrieved = mapper.load(obj); - assertEquals(obj, retrieved); - } - - @Test - public void testIndexKeyWithAutogeneratedAnnotation_DoNotAutogenerateIndexKeyValueIfAlreadySpecified() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - PrimaryKeysNotAutogeneratedIndexKeysAutogenerated obj = new PrimaryKeysNotAutogeneratedIndexKeysAutogenerated(); - - String randomValue = UUID.randomUUID().toString(); - obj.setKey(randomValue); - obj.setRangeKey(randomValue); - obj.setGsiHashKey(randomValue); - obj.setGsiRangeKey(randomValue); - mapper.save(obj); - - // check in-memory value - assertEquals(randomValue, obj.getGsiHashKey()); - assertEquals(randomValue, obj.getGsiRangeKey()); - - PrimaryKeysNotAutogeneratedIndexKeysAutogenerated retrieved = mapper.load(obj); - assertEquals(obj, retrieved); - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class HashKeyRangeKeyBothAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDbAutoGeneratedKey - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAutoGeneratedKey - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - HashKeyRangeKeyBothAutoGenerated other = (HashKeyRangeKeyBothAutoGenerated) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (otherAttribute == null) { - if (other.otherAttribute != null) { - return false; - } - } else if (!otherAttribute.equals(other.otherAttribute)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class HashKeyAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDbAutoGeneratedKey - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - HashKeyAutoGenerated other = (HashKeyAutoGenerated) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (otherAttribute == null) { - if (other.otherAttribute != null) { - return false; - } - } else if (!otherAttribute.equals(other.otherAttribute)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-string-range") - public static class RangeKeyAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAutoGeneratedKey - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - RangeKeyAutoGenerated other = (RangeKeyAutoGenerated) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (otherAttribute == null) { - if (other.otherAttribute != null) { - return false; - } - } else if (!otherAttribute.equals(other.otherAttribute)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class NothingAutoGenerated { - - private String key; - private String rangeKey; - private String otherAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - NothingAutoGenerated other = (NothingAutoGenerated) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (otherAttribute == null) { - if (other.otherAttribute != null) { - return false; - } - } else if (!otherAttribute.equals(other.otherAttribute)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class HashKeyRangeKeyBothAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDbAutoGeneratedKey - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAutoGeneratedKey - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - HashKeyRangeKeyBothAutoGeneratedKeyOnly other = (HashKeyRangeKeyBothAutoGeneratedKeyOnly) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class HashKeyAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDbAutoGeneratedKey - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - HashKeyAutoGeneratedKeyOnly other = (HashKeyAutoGeneratedKeyOnly) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class RangeKeyAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAutoGeneratedKey - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - RangeKeyAutoGeneratedKeyOnly other = (RangeKeyAutoGeneratedKeyOnly) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class NothingAutoGeneratedKeyOnly { - - private String key; - private String rangeKey; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - NothingAutoGeneratedKeyOnly other = (NothingAutoGeneratedKeyOnly) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class PrimaryKeysNotAutogeneratedIndexKeysAutogenerated { - - private String key; - private String rangeKey; - private String gsiHashKey; - private String gsiRangeKey; - - private static boolean isEqual(Object a, Object b) { - if (a == null || b == null) { - return a == null && b == null; - } - return a.equals(b); - } - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbIndexHashKey(globalSecondaryIndexName = GSI_NAME, attributeName = GSI_HASH_KEY) - @DynamoDbAutoGeneratedKey - public String getGsiHashKey() { - return gsiHashKey; - } - - public void setGsiHashKey(String gsiHashKey) { - this.gsiHashKey = gsiHashKey; - } - - @DynamoDbIndexRangeKey(globalSecondaryIndexName = GSI_NAME, attributeName = GSI_RANGE_KEY) - @DynamoDbAutoGeneratedKey - public String getGsiRangeKey() { - return gsiRangeKey; - } - - public void setGsiRangeKey(String gsiRangeKey) { - this.gsiRangeKey = gsiRangeKey; - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof PrimaryKeysNotAutogeneratedIndexKeysAutogenerated)) { - return false; - } - PrimaryKeysNotAutogeneratedIndexKeysAutogenerated other = (PrimaryKeysNotAutogeneratedIndexKeysAutogenerated) object; - - return isEqual(this.getKey(), other.getKey()) - && isEqual(this.getRangeKey(), other.getRangeKey()) - && isEqual(this.getGsiHashKey(), other.getGsiHashKey()) - && isEqual(this.getGsiRangeKey(), other.getGsiRangeKey()); - } - - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AutoGeneratedTimestampIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AutoGeneratedTimestampIntegrationTest.java deleted file mode 100644 index 03048dd2d003..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/AutoGeneratedTimestampIntegrationTest.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGenerateStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedTimestamp; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.KeyAndVal; - -/** - * Tests updating component attribute fields correctly. - */ -public class AutoGeneratedTimestampIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test using {@code Calendar}. - */ - @Test - public void testCalendarType() { - final KeyAndCalendarTimestamp object = new KeyAndCalendarTimestamp(); - assertBeforeAndAfterChange(true, object); - } - - /** - * Test using {@code Date}. - */ - @Test - public void testDateType() { - final KeyAndDateTimestamp object = new KeyAndDateTimestamp(); - - assertBeforeAndAfterChange(true, object); - } - - /** - * Test using a {@code Long}. - */ - @Test - public void testLongType() { - final KeyAndLongTimestamp object = new KeyAndLongTimestamp(); - - assertBeforeAndAfterChange(true, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code ALWAYS}. - */ - @Test - public void testAlwaysStrategy() { - final KeyAndDateTimestamp object = new KeyAndDateTimestamp(); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code ALWAYS}. - */ - @Test - public void testAlwaysStrategyUpdateSkipNullAttribute() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final KeyAndDateTimestamp object = new KeyAndDateTimestamp(); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code CREATE}. - */ - @Test - public void testCreateStrategy() { - final KeyAndOnCreateDateTimestamp object = new KeyAndOnCreateDateTimestamp(); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(false, object); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code CREATE}. - */ - @Test - public void testCreateStrategyUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final KeyAndOnCreateDateTimestamp object = new KeyAndOnCreateDateTimestamp(); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(false, object); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code ALWAYS}. - */ - @Test - public void testAlwaysNoKey() { - final NoKeyAndOnAlwaysDateTimestamp object = new NoKeyAndOnAlwaysDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code ALWAYS}. - */ - @Test - public void testAlwaysNoKeyUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final NoKeyAndOnAlwaysDateTimestamp object = new NoKeyAndOnAlwaysDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(true, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code CREATE}. - */ - @Test - public void testCreateNoKey() { - final NoKeyAndOnCreateDateTimestamp object = new NoKeyAndOnCreateDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - - assertBeforeAndAfterChange(true, object); - assertBeforeAndAfterChange(false, object); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test {@code DynamoDBAutoGenerateStrategy} of {@code CREATE}. - */ - @Test - public void testCreateNoKeyUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final NoKeyAndOnCreateDateTimestamp object = new NoKeyAndOnCreateDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - - assertBeforeAndAfterChange(false, object); - assertBeforeAndAfterChange(false, object); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test the batch save. - */ - @Test - public void testAlwaysOnBatchSave() { - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - objects.add(new KeyAndDateTimestamp()); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testAlwaysOnBatchSaveUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - objects.add(new KeyAndDateTimestamp()); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testCreateOnBatchSave() { - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - objects.add(new KeyAndOnCreateDateTimestamp()); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(false, objects); - assertBeforeAndAfterChange(false, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testCreateOnBatchSaveUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - objects.add(new KeyAndOnCreateDateTimestamp()); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(false, objects); - assertBeforeAndAfterChange(false, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testAlwaysNoKeyOnBatchSave() { - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - final NoKeyAndOnAlwaysDateTimestamp object = new NoKeyAndOnAlwaysDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - objects.add(object); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testAlwaysNoKeyOnBatchSaveUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - final NoKeyAndOnAlwaysDateTimestamp object = new NoKeyAndOnAlwaysDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - objects.add(object); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(true, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testCreateNoKeyOnBatchSave() { - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - final NoKeyAndOnCreateDateTimestamp object = new NoKeyAndOnCreateDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - objects.add(object); - } - - assertBeforeAndAfterChange(true, objects); - assertBeforeAndAfterChange(false, objects); - assertBeforeAndAfterChange(false, objects); - } - - /** - * Test the batch save. - */ - @Test - public void testCreateNoKeyOnBatchSaveUpdateSkipNullAttributes() { - setUpTest(SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - - final List objects = new ArrayList(); - for (int i = 0; i < 10; i++) { - final NoKeyAndOnCreateDateTimestamp object = new NoKeyAndOnCreateDateTimestamp(); - object.setKey(UUID.randomUUID().toString()); - objects.add(object); - } - - assertBeforeAndAfterChange(false, objects); - assertBeforeAndAfterChange(false, objects); - assertBeforeAndAfterChange(false, objects); - } - - /** - * An object with {@code Calendar}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCalendarTimestamp extends AutoKeyAndVal { - @DynamoDbAutoGeneratedTimestamp - public Calendar getVal() { - return super.getVal(); - } - - public void setVal(final Calendar val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateTimestamp extends AutoKeyAndVal { - @DynamoDbAutoGeneratedTimestamp - public Date getVal() { - return super.getVal(); - } - - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Long}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndLongTimestamp extends AutoKeyAndVal { - @DynamoDbAutoGeneratedTimestamp - public Long getVal() { - return super.getVal(); - } - - public void setVal(final Long val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date} only on {@code CREATE}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndOnCreateDateTimestamp extends AutoKeyAndVal { - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.CREATE) - public Date getVal() { - return super.getVal(); - } - - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date} not auto-generted key. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class NoKeyAndOnAlwaysDateTimestamp extends KeyAndVal { - @DynamoDbHashKey - public String getKey() { - return super.getKey(); - } - - public void setKey(final String key) { - super.setKey(key); - } - - @DynamoDbAutoGeneratedTimestamp - public Date getVal() { - return super.getVal(); - } - - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date} not auto-generted key. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class NoKeyAndOnCreateDateTimestamp extends KeyAndVal { - @DynamoDbHashKey - public String getKey() { - return super.getKey(); - } - - public void setKey(final String key) { - super.setKey(key); - } - - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.CREATE) - public Date getVal() { - return super.getVal(); - } - - public void setVal(final Date val) { - super.setVal(val); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/BatchWriteIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/BatchWriteIntegrationTest.java deleted file mode 100644 index d17b90385d4f..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/BatchWriteIntegrationTest.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper.FailedBatch; -import software.amazon.awssdk.services.dynamodb.pojos.BinaryAttributeByteBufferClass; -import software.amazon.awssdk.services.dynamodb.pojos.RangeKeyClass; - -/** - * Tests batch write calls - */ -public class BatchWriteIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = 1; - private static int startKeyDebug = 1; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - } - - @Test - public void testBatchSave() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 40; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - List failedBatches = mapper.batchSave(objs); - - assertEquals(0, failedBatches.size()); - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testBatchSaveAsArray() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 40; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - NumberSetAttributeClass[] objsArray = objs.toArray(new NumberSetAttributeClass[objs.size()]); - mapper.batchSave((Object[]) objsArray); - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testBatchSaveAsListFromArray() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 40; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - NumberSetAttributeClass[] objsArray = objs.toArray(new NumberSetAttributeClass[objs.size()]); - mapper.batchSave(Arrays.asList(objsArray)); - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testBatchDelete() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 40; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.batchSave(objs); - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // Delete the odd ones - int i = 0; - List toDelete = new LinkedList(); - for (NumberSetAttributeClass obj : objs) { - if (i++ % 2 == 0) { - toDelete.add(obj); - } - } - - mapper.batchDelete(toDelete); - - i = 0; - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - if (i++ % 2 == 0) { - assertNull(loaded); - } else { - assertEquals(obj, loaded); - } - } - } - - @Test - public void testBatchSaveAndDelete() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 40; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.batchSave(objs); - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // Delete the odd ones - int i = 0; - List toDelete = new LinkedList(); - for (NumberSetAttributeClass obj : objs) { - if (i++ % 2 == 0) { - toDelete.add(obj); - } - } - - // And add a bunch of new ones - List toSave = new LinkedList(); - for (i = 0; i < 50; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - toSave.add(obj); - } - - mapper.batchWrite(toSave, toDelete); - - i = 0; - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - if (i++ % 2 == 0) { - assertNull(loaded); - } else { - assertEquals(obj, loaded); - } - } - - for (NumberSetAttributeClass obj : toSave) { - NumberSetAttributeClass loaded = mapper.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testMultipleTables() throws Exception { - - List objs = new ArrayList(); - int numItems = 10; - for (int i = 0; i < numItems; i++) { - NumberSetAttributeClass obj = getUniqueNumericObject(); - objs.add(obj); - } - for (int i = 0; i < numItems; i++) { - RangeKeyClass obj = getUniqueRangeKeyObject(); - objs.add(obj); - } - Collections.shuffle(objs); - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - List failedBatches = mapper.batchSave(objs); - assertEquals(failedBatches.size(), 0); - - for (Object obj : objs) { - Object loaded = null; - if (obj instanceof NumberSetAttributeClass) { - loaded = mapper.load(NumberSetAttributeClass.class, ((NumberSetAttributeClass) obj).getKey()); - } else if (obj instanceof RangeKeyClass) { - loaded = mapper.load(RangeKeyClass.class, ((RangeKeyClass) obj).getKey(), - ((RangeKeyClass) obj).getRangeKey()); - } else { - fail(); - } - assertEquals(obj, loaded); - } - - // Delete the odd ones - int i = 0; - List toDelete = new LinkedList(); - for (Object obj : objs) { - if (i++ % 2 == 0) { - toDelete.add(obj); - } - } - - // And add a bunch of new ones - List toSave = new LinkedList(); - for (i = 0; i < numItems; i++) { - if (i % 2 == 0) { - toSave.add(getUniqueNumericObject()); - } else { - toSave.add(getUniqueRangeKeyObject()); - } - } - - failedBatches = mapper.batchWrite(toSave, toDelete); - assertEquals(0, failedBatches.size()); - - i = 0; - for (Object obj : objs) { - Object loaded = null; - if (obj instanceof NumberSetAttributeClass) { - loaded = mapper.load(NumberSetAttributeClass.class, ((NumberSetAttributeClass) obj).getKey()); - } else if (obj instanceof RangeKeyClass) { - loaded = mapper.load(RangeKeyClass.class, ((RangeKeyClass) obj).getKey(), - ((RangeKeyClass) obj).getRangeKey()); - } else { - fail(); - } - - if (i++ % 2 == 0) { - assertNull(loaded); - } else { - assertEquals(obj, loaded); - } - } - - for (Object obj : toSave) { - Object loaded = null; - if (obj instanceof NumberSetAttributeClass) { - loaded = mapper.load(NumberSetAttributeClass.class, ((NumberSetAttributeClass) obj).getKey()); - } else if (obj instanceof RangeKeyClass) { - loaded = mapper.load(RangeKeyClass.class, ((RangeKeyClass) obj).getKey(), - ((RangeKeyClass) obj).getRangeKey()); - } else { - fail(); - } - assertEquals(obj, loaded); - } - } - - /** - * Test whether it finish processing all the items even if the first batch is failed. - */ - @Test - public void testErrorHandling() { - - List objs = new ArrayList(); - int numItems = 25; - - for (int i = 0; i < numItems; i++) { - NoSuchTableClass obj = getuniqueBadObject(); - objs.add(obj); - } - - for (int i = 0; i < numItems; i++) { - RangeKeyClass obj = getUniqueRangeKeyObject(); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - // The failed batch - List failedBatches = mapper.batchSave(objs); - assertEquals(1, failedBatches.size()); - assertEquals(numItems, failedBatches.get(0).getUnprocessedItems().get("tableNotExist").size()); - - // The second batch succeeds, get them back - for (Object obj : objs.subList(25, 50)) { - RangeKeyClass loaded = mapper - .load(RangeKeyClass.class, ((RangeKeyClass) obj).getKey(), ((RangeKeyClass) obj).getRangeKey()); - assertEquals(obj, loaded); - } - } - - /** - * Test whether we can split large batch request into small pieces. - */ - // DynamoDB changed their error for requests that are too large from a - // 413 (RequestEntityTooLarge) to a generic 400 (ValidationException), so - // the mapper's batch-splitting logic is broken. Not sure there's a good - // fix client-side without the service changing back to 413 so we can - // distinguish this case from other ValidationExceptions. - // @Test - public void testLargeRequestEntity() { - - // The total batch size is beyond 1M, test whether our client can split - // the batch correctly - List objs = new ArrayList(); - - int numItems = 25; - final int CONTENT_LENGTH = 1024 * 25; - - for (int i = 0; i < numItems; i++) { - BinaryAttributeByteBufferClass obj = getUniqueByteBufferObject(CONTENT_LENGTH); - objs.add(obj); - } - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - List failedBatches = mapper.batchSave(objs); - assertEquals(0, failedBatches.size()); - - // Get these objects back - for (BinaryAttributeByteBufferClass obj : objs) { - BinaryAttributeByteBufferClass loaded = mapper.load(BinaryAttributeByteBufferClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // There are three super large item together with some small ones, test - // whether we can successfully - // save these small items. - objs.clear(); - numItems = 10; - List largeObjs = new ArrayList(); - - // Put three super large item(beyond 64k) - largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); - largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); - largeObjs.add(getUniqueByteBufferObject(CONTENT_LENGTH * 30)); - for (int i = 0; i < numItems - 3; i++) { - BinaryAttributeByteBufferClass obj = getUniqueByteBufferObject(CONTENT_LENGTH / 25); - objs.add(obj); - } - - objs.addAll(largeObjs); - - failedBatches = mapper.batchSave(objs); - assertEquals(3, failedBatches.size()); - objs.removeAll(largeObjs); - - // Get these small objects back - for (BinaryAttributeByteBufferClass obj : objs) { - BinaryAttributeByteBufferClass loaded = mapper.load(BinaryAttributeByteBufferClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - // The whole batch is super large objects, none of them will be - // processed - largeObjs.clear(); - for (int i = 0; i < 5; i++) { - BinaryAttributeByteBufferClass obj = getUniqueByteBufferObject(CONTENT_LENGTH * 30); - largeObjs.add(obj); - } - failedBatches = mapper.batchSave(largeObjs); - assertEquals(5, failedBatches.size()); - } - - - private NoSuchTableClass getuniqueBadObject() { - NoSuchTableClass obj = new NoSuchTableClass(); - obj.setKey(String.valueOf(startKeyDebug++)); - return obj; - } - - private NumberSetAttributeClass getUniqueNumericObject() { - NumberSetAttributeClass obj = new NumberSetAttributeClass(); - obj.setKey(String.valueOf(startKeyDebug++)); - obj.setBigDecimalAttribute(toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); - obj.setBigIntegerAttribute( - toSet(new BigInteger("" + startKey++), new BigInteger("" + startKey++), new BigInteger("" + startKey++))); - obj.setByteObjectAttribute(toSet(new Byte(nextByte()), new Byte(nextByte()), new Byte(nextByte()))); - obj.setDoubleObjectAttribute(toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); - obj.setFloatObjectAttribute(toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); - obj.setIntegerAttribute(toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); - obj.setLongObjectAttribute(toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); - obj.setBooleanAttribute(toSet(true, false)); - obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); - Set cals = new HashSet(); - for (Date d : obj.getDateAttribute()) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(d); - cals.add(cal); - } - obj.setCalendarAttribute(toSet(cals)); - return obj; - } - - private RangeKeyClass getUniqueRangeKeyObject() { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(startKey++); - obj.setIntegerAttribute(toSet(start++, start++, start++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setRangeKey(start++); - obj.setStringAttribute("" + startKey++); - obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); - return obj; - } - - private String nextByte() { - return "" + byteStart++ % Byte.MAX_VALUE; - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/BinaryAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/BinaryAttributesIntegrationTest.java deleted file mode 100644 index 32a95998d2be..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/BinaryAttributesIntegrationTest.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.pojos.BinaryAttributeByteArrayClass; -import software.amazon.awssdk.services.dynamodb.pojos.BinaryAttributeByteBufferClass; - -/** - * Tests simple string attributes - */ -public class BinaryAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String BINARY_ATTRIBUTE = "binaryAttribute"; - private static final String BINARY_SET_ATTRIBUTE = "binarySetAttribute"; - private static final List> ATTRIBUTES = new LinkedList>(); - private static final int CONTENT_LENGTH = 512; - - // Test data - static { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().s("" + startKey++).build()); - attr.put(BINARY_ATTRIBUTE, AttributeValue.builder().b(SdkBytes.fromByteArray(generateByteArray(CONTENT_LENGTH))).build()); - attr.put(BINARY_SET_ATTRIBUTE, AttributeValue.builder().bs(SdkBytes.fromByteArray(generateByteArray(CONTENT_LENGTH)), - SdkBytes.fromByteArray(generateByteArray(CONTENT_LENGTH + 1))).build()); - ATTRIBUTES.add(attr); - - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperIntegrationTestBase.setUp(); - - // Insert the data - for (Map attr : ATTRIBUTES) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(attr).build()); - } - } - - @Test - public void testLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - for (Map attr : ATTRIBUTES) { - // test BinaryAttributeClass - BinaryAttributeByteBufferClass x = util.load(BinaryAttributeByteBufferClass.class, attr.get(KEY_NAME).s()); - assertEquals(x.getKey(), attr.get(KEY_NAME).s()); - assertEquals(x.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH))); - assertTrue(x.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH)))); - assertTrue(x.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH + 1)))); - - // test BinaryAttributeByteArrayClass - BinaryAttributeByteArrayClass y = util.load(BinaryAttributeByteArrayClass.class, attr.get(KEY_NAME).s()); - assertEquals(y.getKey(), attr.get(KEY_NAME).s()); - assertTrue(Arrays.equals(y.getBinaryAttribute(), (generateByteArray(CONTENT_LENGTH)))); - assertEquals(2, y.getBinarySetAttribute().size()); - assertTrue(setContainsBytes(y.getBinarySetAttribute(), generateByteArray(CONTENT_LENGTH))); - assertTrue(setContainsBytes(y.getBinarySetAttribute(), generateByteArray(CONTENT_LENGTH + 1))); - } - - } - - @Test - public void testSave() { - // test BinaryAttributeClass - List byteBufferObjs = new ArrayList(); - for (int i = 0; i < 5; i++) { - BinaryAttributeByteBufferClass obj = getUniqueByteBufferObject(CONTENT_LENGTH); - byteBufferObjs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (BinaryAttributeByteBufferClass obj : byteBufferObjs) { - util.save(obj); - } - - for (BinaryAttributeByteBufferClass obj : byteBufferObjs) { - BinaryAttributeByteBufferClass loaded = util.load(BinaryAttributeByteBufferClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertEquals(loaded.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH))); - assertTrue(loaded.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH)))); - } - - // test BinaryAttributeByteArrayClass - List bytesObjs = new ArrayList(); - for (int i = 0; i < 5; i++) { - BinaryAttributeByteArrayClass obj = getUniqueBytesObject(CONTENT_LENGTH); - bytesObjs.add(obj); - } - - for (BinaryAttributeByteArrayClass obj : bytesObjs) { - util.save(obj); - } - - for (BinaryAttributeByteArrayClass obj : bytesObjs) { - BinaryAttributeByteArrayClass loaded = util.load(BinaryAttributeByteArrayClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertTrue(Arrays.equals(loaded.getBinaryAttribute(), (generateByteArray(CONTENT_LENGTH)))); - assertEquals(1, loaded.getBinarySetAttribute().size()); - assertTrue(setContainsBytes(loaded.getBinarySetAttribute(), generateByteArray(CONTENT_LENGTH))); - } - } - - /** - * Tests saving an incomplete object into DynamoDB - */ - @Test - public void testIncompleteObject() { - // test BinaryAttributeClass - BinaryAttributeByteBufferClass byteBufferObj = getUniqueByteBufferObject(CONTENT_LENGTH); - byteBufferObj.setBinarySetAttribute(null); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(byteBufferObj); - - BinaryAttributeByteBufferClass loadedX = util.load(BinaryAttributeByteBufferClass.class, byteBufferObj.getKey()); - assertEquals(loadedX.getKey(), byteBufferObj.getKey()); - assertEquals(loadedX.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH))); - assertEquals(loadedX.getBinarySetAttribute(), null); - - - // test removing an attribute - assertNotNull(byteBufferObj.getBinaryAttribute()); - byteBufferObj.setBinaryAttribute(null); - util.save(byteBufferObj); - - loadedX = util.load(BinaryAttributeByteBufferClass.class, byteBufferObj.getKey()); - assertEquals(loadedX.getKey(), byteBufferObj.getKey()); - assertEquals(loadedX.getBinaryAttribute(), null); - assertEquals(loadedX.getBinarySetAttribute(), null); - - // test BinaryAttributeByteArrayClass - BinaryAttributeByteArrayClass bytesObj = getUniqueBytesObject(CONTENT_LENGTH); - bytesObj.setBinarySetAttribute(null); - util.save(bytesObj); - - BinaryAttributeByteArrayClass loadedY = util.load(BinaryAttributeByteArrayClass.class, bytesObj.getKey()); - assertEquals(loadedY.getKey(), bytesObj.getKey()); - assertTrue(Arrays.equals(loadedY.getBinaryAttribute(), generateByteArray(CONTENT_LENGTH))); - assertEquals(loadedY.getBinarySetAttribute(), null); - - - // test removing an attribute - assertNotNull(bytesObj.getBinaryAttribute()); - bytesObj.setBinaryAttribute(null); - util.save(bytesObj); - - loadedY = util.load(BinaryAttributeByteArrayClass.class, bytesObj.getKey()); - assertEquals(loadedY.getKey(), bytesObj.getKey()); - assertEquals(loadedY.getBinaryAttribute(), null); - assertEquals(loadedY.getBinarySetAttribute(), null); - } - - @Test - public void testUpdate() { - // test BinaryAttributeClass - List byteBufferObjs = new ArrayList(); - for (int i = 0; i < 5; i++) { - BinaryAttributeByteBufferClass obj = getUniqueByteBufferObject(CONTENT_LENGTH); - byteBufferObjs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (BinaryAttributeByteBufferClass obj : byteBufferObjs) { - util.save(obj); - } - - for (BinaryAttributeByteBufferClass obj : byteBufferObjs) { - BinaryAttributeByteBufferClass replacement = getUniqueByteBufferObject(CONTENT_LENGTH - 1); - replacement.setKey(obj.getKey()); - util.save(replacement); - - BinaryAttributeByteBufferClass loaded = util.load(BinaryAttributeByteBufferClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertEquals(loaded.getBinaryAttribute(), ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH - 1))); - assertTrue(loaded.getBinarySetAttribute().contains(ByteBuffer.wrap(generateByteArray(CONTENT_LENGTH - 1)))); - - } - - // test BinaryAttributeByteArrayClass - List bytesObj = new ArrayList(); - for (int i = 0; i < 5; i++) { - BinaryAttributeByteArrayClass obj = getUniqueBytesObject(CONTENT_LENGTH); - bytesObj.add(obj); - } - - for (BinaryAttributeByteArrayClass obj : bytesObj) { - util.save(obj); - } - - for (BinaryAttributeByteArrayClass obj : bytesObj) { - BinaryAttributeByteArrayClass replacement = getUniqueBytesObject(CONTENT_LENGTH - 1); - replacement.setKey(obj.getKey()); - util.save(replacement); - - BinaryAttributeByteArrayClass loaded = util.load(BinaryAttributeByteArrayClass.class, obj.getKey()); - assertEquals(loaded.getKey(), obj.getKey()); - assertTrue(Arrays.equals(loaded.getBinaryAttribute(), (generateByteArray(CONTENT_LENGTH - 1)))); - assertEquals(1, loaded.getBinarySetAttribute().size()); - assertTrue(setContainsBytes(loaded.getBinarySetAttribute(), generateByteArray(CONTENT_LENGTH - 1))); - - } - } - - @Test - @Ignore // FIXME: Mapper needs to be be updated to be aware of AutoConstructMap - public void testDelete() throws Exception { - // test BinaryAttributeClass - BinaryAttributeByteBufferClass byteBufferObj = getUniqueByteBufferObject(CONTENT_LENGTH); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(byteBufferObj); - - util.delete(byteBufferObj); - assertNull(util.load(BinaryAttributeByteBufferClass.class, byteBufferObj.getKey())); - - // test BinaryAttributeByteArrayClass - BinaryAttributeByteArrayClass bytesObj = getUniqueBytesObject(CONTENT_LENGTH); - util.save(bytesObj); - - util.delete(bytesObj); - assertNull(util.load(BinaryAttributeByteArrayClass.class, bytesObj.getKey())); - - } - - private BinaryAttributeByteArrayClass getUniqueBytesObject(int contentLength) { - BinaryAttributeByteArrayClass obj = new BinaryAttributeByteArrayClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBinaryAttribute(generateByteArray(contentLength)); - Set byteArray = new HashSet(); - byteArray.add(generateByteArray(contentLength)); - obj.setBinarySetAttribute(byteArray); - return obj; - } - - private boolean setContainsBytes(Set set, byte[] bytes) { - Iterator iter = set.iterator(); - while (iter.hasNext()) { - if (Arrays.equals(iter.next(), bytes)) { - return true; - } - } - return false; - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ComplexTypeIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ComplexTypeIntegrationTest.java deleted file mode 100644 index 91cc92f72c57..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ComplexTypeIntegrationTest.java +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.MappingJsonFactory; -import java.io.StringReader; -import java.io.StringWriter; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConverted; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConvertedJson; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConverter; - -/** - * Tests of the configuration object - */ -public class ComplexTypeIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = -127; - - @Test - public void testComplexTypes() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - ComplexClass obj = getUniqueObject(); - util.save(obj); - ComplexClass loaded = util.load(ComplexClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - - private ComplexClass getUniqueObject() { - ComplexClass obj = new ComplexClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); - obj.setByteAttribute((byte) byteStart++); - obj.setByteObjectAttribute(new Byte("" + byteStart++)); - obj.setDoubleAttribute(new Double("" + start++)); - obj.setDoubleObjectAttribute(new Double("" + start++)); - obj.setFloatAttribute(new Float("" + start++)); - obj.setFloatObjectAttribute(new Float("" + start++)); - obj.setIntAttribute(new Integer("" + start++)); - obj.setIntegerAttribute(new Integer("" + start++)); - obj.setLongAttribute(new Long("" + start++)); - obj.setLongObjectAttribute(new Long("" + start++)); - obj.setDateAttribute(new Date(startKey++)); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanObjectAttribute(start++ % 2 == 0); - obj.setExtraField("" + startKey++); - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(new Date(startKey++)); - obj.setCalendarAttribute(cal); - obj.setComplexNestedType(new ComplexNestedType("" + start++, start++, new ComplexNestedType("" + start++, - start++, null))); - List complexTypes = new ArrayList(); - complexTypes.add(new ComplexNestedType("" + start++, start++, - new ComplexNestedType("" + start++, start++, null))); - complexTypes.add(new ComplexNestedType("" + start++, start++, new ComplexNestedType("" + start++, start++, null))); - complexTypes.add(new ComplexNestedType("" + start++, start++, new ComplexNestedType("" + start++, start++, null))); - obj.setComplexNestedTypeList(complexTypes); - return obj; - } - - /** - * Tests using a complex type for a (string) key - */ - @Test - public void testComplexKey() throws Exception { - ComplexKey obj = new ComplexKey(); - ComplexNestedType key = new ComplexNestedType(); - key.setIntValue(start++); - key.setStringValue("" + start++); - obj.setKey(key); - obj.setOtherAttribute("" + start++); - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - mapper.save(obj); - ComplexKey loaded = mapper.load(ComplexKey.class, obj.getKey()); - assertEquals(obj, loaded); - } - - public static final class ComplexNestedListTypeMarshaller implements DynamoDbTypeConverter> { - @Override - public String convert(final List object) { - try { - StringWriter writer = new StringWriter(); - JsonFactory jsonFactory = new MappingJsonFactory(); - JsonGenerator jsonGenerator = jsonFactory.createJsonGenerator(writer); - jsonGenerator.writeObject(object); - return writer.toString(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public List unconvert(String obj) { - try { - JsonFactory jsonFactory = new MappingJsonFactory(); - JsonParser jsonParser = jsonFactory.createJsonParser(new StringReader(obj)); - return jsonParser.readValueAs(new TypeReference>() { - }); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static final class ComplexClass extends NumberAttributeClass { - - private String extraField; - private ComplexNestedType complexNestedType; - private List complexNestedTypeList; - - @DynamoDbTypeConvertedJson - public ComplexNestedType getComplexNestedType() { - return complexNestedType; - } - - public void setComplexNestedType(ComplexNestedType complexNestedType) { - this.complexNestedType = complexNestedType; - } - - @DynamoDbTypeConverted(converter = ComplexNestedListTypeMarshaller.class) - public List getComplexNestedTypeList() { - return complexNestedTypeList; - } - - public void setComplexNestedTypeList(List complexNestedTypeList) { - this.complexNestedTypeList = complexNestedTypeList; - } - - public String getExtraField() { - return extraField; - } - - public void setExtraField(String extraField) { - this.extraField = extraField; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((complexNestedType == null) ? 0 : complexNestedType.hashCode()); - result = prime * result + ((complexNestedTypeList == null) ? 0 : complexNestedTypeList.hashCode()); - result = prime * result + ((extraField == null) ? 0 : extraField.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ComplexClass other = (ComplexClass) obj; - if (complexNestedType == null) { - if (other.complexNestedType != null) { - return false; - } - } else if (!complexNestedType.equals(other.complexNestedType)) { - return false; - } - if (complexNestedTypeList == null) { - if (other.complexNestedTypeList != null) { - return false; - } - } else if (!complexNestedTypeList.equals(other.complexNestedTypeList)) { - return false; - } - if (extraField == null) { - if (other.extraField != null) { - return false; - } - } else if (!extraField.equals(other.extraField)) { - return false; - } - return true; - } - - } - - public static final class ComplexNestedType { - - private String stringValue; - private Integer intValue; - private ComplexNestedType nestedType; - - public ComplexNestedType() { - } - - public ComplexNestedType(String stringValue, Integer intValue, ComplexNestedType nestedType) { - super(); - this.stringValue = stringValue; - this.intValue = intValue; - this.nestedType = nestedType; - } - - public String getStringValue() { - return stringValue; - } - - public void setStringValue(String stringValue) { - this.stringValue = stringValue; - } - - public Integer getIntValue() { - return intValue; - } - - public void setIntValue(Integer intValue) { - this.intValue = intValue; - } - - public ComplexNestedType getNestedType() { - return nestedType; - } - - public void setNestedType(ComplexNestedType nestedType) { - this.nestedType = nestedType; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((intValue == null) ? 0 : intValue.hashCode()); - result = prime * result + ((nestedType == null) ? 0 : nestedType.hashCode()); - result = prime * result + ((stringValue == null) ? 0 : stringValue.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ComplexNestedType other = (ComplexNestedType) obj; - if (intValue == null) { - if (other.intValue != null) { - return false; - } - } else if (!intValue.equals(other.intValue)) { - return false; - } - if (nestedType == null) { - if (other.nestedType != null) { - return false; - } - } else if (!nestedType.equals(other.nestedType)) { - return false; - } - if (stringValue == null) { - if (other.stringValue != null) { - return false; - } - } else if (!stringValue.equals(other.stringValue)) { - return false; - } - return true; - } - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static final class ComplexKey { - - private ComplexNestedType key; - private String otherAttribute; - - @DynamoDbHashKey - @DynamoDbTypeConvertedJson - public ComplexNestedType getKey() { - return key; - } - - public void setKey(ComplexNestedType key) { - this.key = key; - } - - public String getOtherAttribute() { - return otherAttribute; - } - - public void setOtherAttribute(String otherAttribute) { - this.otherAttribute = otherAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherAttribute == null) ? 0 : otherAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ComplexKey other = (ComplexKey) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (otherAttribute == null) { - if (other.otherAttribute != null) { - return false; - } - } else if (!otherAttribute.equals(other.otherAttribute)) { - return false; - } - return true; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ConfigurationIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ConfigurationIntegrationTest.java deleted file mode 100644 index ebb4eb1f7a40..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ConfigurationIntegrationTest.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.ObjectTableNameResolver; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.TableNameOverride; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.TableNameResolver; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Tests of the configuration object - */ -public class ConfigurationIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = -127; - - @Test - public void testClobber() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo, new DynamoDbMapperConfig(SaveBehavior.CLOBBER)); - - NumberAttributeClassExtended obj = getUniqueObject(); - util.save(obj); - assertEquals(obj, util.load(obj.getClass(), obj.getKey())); - - NumberAttributeClass copy = copy(obj); - util.save(copy); - assertEquals(copy, util.load(copy.getClass(), obj.getKey())); - - // We should have lost the extra field because of the clobber behavior - assertNull(util.load(NumberAttributeClassExtended.class, obj.getKey()).getExtraField()); - - // Now test overriding the clobber behavior on a per-save basis - obj = getUniqueObject(); - util.save(obj); - assertEquals(obj, util.load(obj.getClass(), obj.getKey())); - - copy = copy(obj); - util.save(copy, new DynamoDbMapperConfig(SaveBehavior.UPDATE)); - assertEquals(copy, util.load(copy.getClass(), obj.getKey())); - - // We shouldn't have lost any extra info - assertNotNull(util.load(NumberAttributeClassExtended.class, obj.getKey()).getExtraField()); - } - - @Test - public void testTableOverride() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - TableOverrideTestClass obj = new TableOverrideTestClass(); - obj.setOtherField(UUID.randomUUID().toString()); - - try { - util.save(obj); - fail("Expected an exception"); - } catch (Exception e) { - // Ignored or expected. - } - - util.save(obj, new DynamoDbMapperConfig(new TableNameOverride("aws-java-sdk-util"))); - - try { - util.load(TableOverrideTestClass.class, obj.getKey()); - fail("Expected an exception"); - } catch (Exception e) { - // Ignored or expected. - } - - Object loaded = util.load(TableOverrideTestClass.class, obj.getKey(), - new DynamoDbMapperConfig(TableNameOverride.withTableNamePrefix("aws-"))); - assertEquals(loaded, obj); - - try { - util.delete(obj); - fail("Expected an exception"); - } catch (Exception e) { - // Ignored or expected. - } - - util.delete(obj, new DynamoDbMapperConfig(TableNameOverride.withTableNamePrefix("aws-"))); - } - - @Test - public void testTableNameResolver() { - final String REAL_TABLE_NAME = "aws-java-sdk-util"; - - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - final TableOverrideTestClass obj = new TableOverrideTestClass(); - obj.setOtherField(UUID.randomUUID().toString()); - - try { - mapper.save(obj); - fail("Expected an exception, because the POJO is annotated with a non-existent table."); - } catch (Exception e) { - // Ignored or expected. - } - - // Use TableNameResolver to save to the real table - mapper.save(obj, new DynamoDbMapperConfig(new TableNameResolver() { - @Override - public String getTableName(Class clazz, DynamoDbMapperConfig config) { - if (clazz.equals(TableOverrideTestClass.class)) { - return REAL_TABLE_NAME; - } - throw new RuntimeException("Unexpected data object type."); - } - })); - - try { - mapper.load(TableOverrideTestClass.class, obj.getKey()); - fail("Expected an exception, because the POJO is annotated with a non-existent table."); - } catch (Exception e) { - // Ignored or expected. - } - - // Use ObjectTableNameResolver to load from the real table - Object loaded = mapper.load(obj, - new DynamoDbMapperConfig(new ObjectTableNameResolver() { - @Override - public String getTableName(Object objectToLoad, DynamoDbMapperConfig config) { - if (objectToLoad == obj) { - return REAL_TABLE_NAME; - } - throw new RuntimeException("Unexpected data object."); - } - })); - assertEquals(loaded, obj); - - try { - mapper.delete(obj); - fail("Expected an exception, because the POJO is annotated with a non-existent table."); - } catch (Exception e) { - // Ignored or expected. - } - - // When used at the same time, ObjectTableNameResolver should have the highest priority - - final String NON_EXISTENT_TABLE_NAME = UUID.randomUUID().toString(); - mapper.delete(obj, new DynamoDbMapperConfig.Builder() - .withTableNameOverride(new TableNameOverride(NON_EXISTENT_TABLE_NAME)) - .withTableNameResolver(new TableNameResolver() { - @Override - public String getTableName(Class clazz, DynamoDbMapperConfig config) { - return NON_EXISTENT_TABLE_NAME; - } - }) - .withObjectTableNameResolver(new ObjectTableNameResolver() { - @Override - public String getTableName(Object object, DynamoDbMapperConfig config) { - return REAL_TABLE_NAME; - } - }).build()); - } - - private NumberAttributeClassExtended getUniqueObject() { - NumberAttributeClassExtended obj = new NumberAttributeClassExtended(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); - obj.setByteAttribute((byte) byteStart++); - obj.setByteObjectAttribute(new Byte("" + byteStart++)); - obj.setDoubleAttribute(new Double("" + start++)); - obj.setDoubleObjectAttribute(new Double("" + start++)); - obj.setFloatAttribute(new Float("" + start++)); - obj.setFloatObjectAttribute(new Float("" + start++)); - obj.setIntAttribute(new Integer("" + start++)); - obj.setIntegerAttribute(new Integer("" + start++)); - obj.setLongAttribute(new Long("" + start++)); - obj.setLongObjectAttribute(new Long("" + start++)); - obj.setDateAttribute(new Date(startKey++)); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanObjectAttribute(start++ % 2 == 0); - obj.setExtraField("" + startKey++); - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(new Date(startKey++)); - obj.setCalendarAttribute(cal); - return obj; - } - - private NumberAttributeClass copy(NumberAttributeClassExtended obj) { - NumberAttributeClass copy = new NumberAttributeClass(); - copy.setKey(obj.getKey()); - copy.setBigDecimalAttribute(obj.getBigDecimalAttribute()); - copy.setBigIntegerAttribute(obj.getBigIntegerAttribute()); - copy.setByteAttribute(obj.getByteAttribute()); - copy.setByteObjectAttribute(obj.getByteObjectAttribute()); - copy.setDoubleAttribute(obj.getDoubleAttribute()); - copy.setDoubleObjectAttribute(obj.getDoubleObjectAttribute()); - copy.setFloatAttribute(obj.getFloatAttribute()); - copy.setFloatObjectAttribute(obj.getFloatObjectAttribute()); - copy.setIntAttribute(obj.getIntAttribute()); - copy.setIntegerAttribute(obj.getIntegerAttribute()); - copy.setLongAttribute(obj.getLongAttribute()); - copy.setLongObjectAttribute(obj.getLongObjectAttribute()); - copy.setDateAttribute(obj.getDateAttribute()); - copy.setBooleanAttribute(obj.isBooleanAttribute()); - copy.setBooleanObjectAttribute(obj.getBooleanObjectAttribute()); - return copy; - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static final class NumberAttributeClassExtended extends NumberAttributeClass { - - private String extraField; - - public String getExtraField() { - return extraField; - } - - public void setExtraField(String extraField) { - this.extraField = extraField; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((extraField == null) ? 0 : extraField.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - NumberAttributeClassExtended other = (NumberAttributeClassExtended) obj; - if (extraField == null) { - if (other.extraField != null) { - return false; - } - } else if (!extraField.equals(other.extraField)) { - return false; - } - return true; - } - } - - @DynamoDbTable(tableName = "java-sdk-util") // doesn't exist - public static final class TableOverrideTestClass { - - private String key; - private String otherField; - - @DynamoDbAutoGeneratedKey - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getOtherField() { - return otherField; - } - - public void setOtherField(String otherField) { - this.otherField = otherField; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((otherField == null) ? 0 : otherField.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - TableOverrideTestClass other = (TableOverrideTestClass) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (otherField == null) { - if (other.otherField != null) { - return false; - } - } else if (!otherField.equals(other.otherField)) { - return false; - } - return true; - } - - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/CrossSdkIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/CrossSdkIntegrationTest.java deleted file mode 100644 index b6830c4acb60..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/CrossSdkIntegrationTest.java +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.pojos.CrossSdkVerificationClass; - - -/** - * Cross-SDK acceptance test. More of a smoke test, verifies that the formats - * used by each program's ORM can be read by the others. - */ -public class CrossSdkIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String TABLE_NAME = "aws-xsdk"; - - private static final String HASH_KEY = "3530a51a-0760-47d2-bfcb-158320d6188a"; - private static final String RANGE_KEY = "61cdf81e-792f-4dd8-a812-a16185bfbf60"; - - private static int start = 1; - - // @BeforeClass - public static void setUp() throws Exception { - setUpCredentials(); - dynamo = DynamoDbClient.builder().credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - - // Create a table - String keyName = DynamoDBMapperIntegrationTestBase.KEY_NAME; - String rangeKey = "rangeKey"; - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_NAME) - .keySchema(KeySchemaElement.builder().attributeName(keyName).keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName(rangeKey).keyType(KeyType.RANGE).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(keyName).attributeType( - ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName(rangeKey).attributeType( - ScalarAttributeType.S).build()) - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L) - .writeCapacityUnits(10L).build()) - .build(); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - } - } - - @Test - public void disabled() { - } - - // This record written by the .NET mapper no longer exists, so this test - // NPEs. If we want to add back something similar we should generate some - // items using the .NET mapper and check a serialized form of them into - // this package so this can be run as a unit test. - // @Test - public void testLoad() throws Exception { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - CrossSdkVerificationClass obj = mapper.load(CrossSdkVerificationClass.class, HASH_KEY, RANGE_KEY); - - Long originalVersion = obj.getVersion(); - - assertNotNull(obj); - assertNotNull(obj.getKey()); - assertEquals(obj.getKey(), HASH_KEY); - assertNotNull(obj.getRangeKey()); - assertEquals(obj.getRangeKey(), RANGE_KEY); - assertNotNull(originalVersion); - assertNotNull(obj.bigDecimalAttribute()); - assertNotNull(obj.bigDecimalSetAttribute()); - assertEquals(3, obj.bigDecimalSetAttribute().size()); - assertNotNull(obj.bigIntegerAttribute()); - assertNotNull(obj.bigIntegerSetAttribute()); - assertEquals(3, obj.bigIntegerSetAttribute().size()); - assertNotNull(obj.booleanAttribute()); - assertNotNull(obj.booleanSetAttribute()); - assertEquals(2, obj.booleanSetAttribute().size()); - assertNotNull(obj.byteAttribute()); - assertNotNull(obj.byteSetAttribute()); - assertEquals(3, obj.byteSetAttribute().size()); - assertNotNull(obj.getCalendarAttribute()); - assertNotNull(obj.getCalendarSetAttribute()); - assertEquals(3, obj.getCalendarSetAttribute().size()); - assertNotNull(obj.getDateAttribute()); - assertNotNull(obj.getDateSetAttribute()); - assertEquals(3, obj.getDateSetAttribute().size()); - assertNotNull(obj.getDoubleAttribute()); - assertNotNull(obj.getDoubleSetAttribute()); - assertEquals(3, obj.getDoubleSetAttribute().size()); - assertNotNull(obj.getFloatAttribute()); - assertNotNull(obj.getFloatSetAttribute()); - assertEquals(3, obj.getFloatSetAttribute().size()); - assertNotNull(obj.getIntegerAttribute()); - assertNotNull(obj.getIntegerSetAttribute()); - assertEquals(3, obj.getIntegerSetAttribute().size()); - assertNotNull(obj.longAttribute()); - assertNotNull(obj.longSetAttribute()); - assertEquals(3, obj.longSetAttribute().size()); - assertNotNull(obj.stringSetAttribute()); - assertEquals(3, obj.stringSetAttribute().size()); - - updateObjectValues(obj); - - mapper.save(obj); - assertFalse(originalVersion.equals(obj.getVersion())); - - CrossSdkVerificationClass loaded = mapper.load(CrossSdkVerificationClass.class, HASH_KEY, RANGE_KEY); - assertEquals(loaded, obj); - } - - /** - * Updates all values in the object (except for the keys and version) - */ - private void updateObjectValues(CrossSdkVerificationClass obj) { - obj.setBigDecimalAttribute(obj.bigDecimalAttribute().add(BigDecimal.ONE)); - Set bigDecimals = new HashSet(); - for (BigDecimal d : obj.bigDecimalSetAttribute()) { - bigDecimals.add(d.add(BigDecimal.ONE)); - } - obj.setBigDecimalSetAttribute(bigDecimals); - - obj.setBigIntegerAttribute(obj.bigIntegerAttribute().add(BigInteger.ONE)); - Set bigInts = new HashSet(); - for (BigInteger d : obj.bigIntegerSetAttribute()) { - bigInts.add(d.add(BigInteger.ONE)); - } - obj.setBigIntegerSetAttribute(bigInts); - - obj.setBooleanAttribute(!obj.booleanAttribute()); - - obj.setByteAttribute((byte) ((obj.byteAttribute() + 1) % Byte.MAX_VALUE)); - Set bytes = new HashSet(); - for (Byte b : obj.byteSetAttribute()) { - bytes.add((byte) ((b + 1) % Byte.MAX_VALUE)); - } - - obj.getCalendarAttribute().setTime(new Date(obj.getCalendarAttribute().getTimeInMillis() + 1000)); - for (Calendar c : obj.getCalendarSetAttribute()) { - c.setTime(new Date(c.getTimeInMillis() + 1000)); - } - - obj.getDateAttribute().setTime(obj.getDateAttribute().getTime() + 1000); - for (Date d : obj.getDateSetAttribute()) { - d.setTime(d.getTime() + 1000); - } - - obj.setDoubleAttribute(obj.getDoubleAttribute() + 1.0); - Set doubleSet = new HashSet(); - for (Double d : obj.getDoubleSetAttribute()) { - doubleSet.add(d + 1.0); - } - obj.setDoubleSetAttribute(doubleSet); - - obj.setFloatAttribute((float) (obj.getFloatAttribute() + 1.0)); - Set floatSet = new HashSet(); - for (Float f : obj.getFloatSetAttribute()) { - floatSet.add(f + 1.0f); - } - obj.setFloatSetAttribute(floatSet); - - obj.setIntegerAttribute(obj.getIntegerAttribute() + 1); - Set intSet = new HashSet(); - for (Integer i : obj.getIntegerSetAttribute()) { - intSet.add(i + 1); - } - obj.setIntegerSetAttribute(intSet); - - obj.setLastUpdater("java-sdk"); - - obj.setLongAttribute(obj.longAttribute() + 1); - Set longSet = new HashSet(); - for (Long l : obj.longSetAttribute()) { - longSet.add(l + 1); - } - obj.setLongSetAttribute(longSet); - - obj.setStringSetAttribute( - toSet(UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString())); - } - - /** - * Used to set up the original object, no longer used. - */ - @SuppressWarnings("unused") - private CrossSdkVerificationClass getUniqueObject() { - CrossSdkVerificationClass obj = new CrossSdkVerificationClass(); - obj.setKey(HASH_KEY); - obj.setRangeKey(RANGE_KEY); - obj.setBigDecimalAttribute(new BigDecimal(start++)); - obj.setBigDecimalSetAttribute(toSet(new BigDecimal(start++), new BigDecimal(start++), new BigDecimal(start++))); - obj.setBigIntegerAttribute(new BigInteger("" + start++)); - obj.setBigIntegerSetAttribute( - toSet(new BigInteger("" + start++), new BigInteger("" + start++), new BigInteger("" + start++))); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanSetAttribute(toSet(true, false)); - obj.setByteAttribute((byte) start++); - obj.setByteSetAttribute(toSet((byte) start++, (byte) start++, (byte) start++)); - obj.setCalendarAttribute(getUniqueCalendar()); - obj.setCalendarSetAttribute(toSet(getUniqueCalendar(), getUniqueCalendar(), getUniqueCalendar())); - obj.setDateAttribute(new Date(start++)); - obj.setDateSetAttribute(toSet(new Date(start++), new Date(start++), new Date(start++))); - obj.setDoubleAttribute((double) start++); - obj.setDoubleSetAttribute(toSet((double) start++, (double) start++, (double) start++)); - obj.setFloatAttribute((float) start++); - obj.setFloatSetAttribute(toSet((float) start++, (float) start++, (float) start++)); - obj.setIntegerAttribute(start++); - obj.setIntegerSetAttribute(toSet(start++, start++, start++)); - obj.setLongAttribute((long) start++); - obj.setLongSetAttribute(toSet((long) start++, (long) start++, (long) start++)); - obj.setStringSetAttribute(toSet("" + start++, "" + start++, "" + start++)); - return obj; - } - - private Calendar getUniqueCalendar() { - Calendar cal = Calendar.getInstance(); - cal.setTime(new Date(start++)); - return cal; - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/DelimitedIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/DelimitedIntegrationTest.java deleted file mode 100644 index 2aff58fdef8e..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/DelimitedIntegrationTest.java +++ /dev/null @@ -1,486 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.util.Collections; -import java.util.Date; -import java.util.Set; -import java.util.UUID; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedTimestamp; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbDelimited; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConvertedTimestamp; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.Currency; -import software.amazon.awssdk.services.dynamodb.pojos.DateRange; -import software.amazon.awssdk.services.dynamodb.pojos.KeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.PhoneNumber; - -/** - * Tests updating component attribute fields correctly. - */ -public class DelimitedIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testPhoneNumber() { - final KeyAndPhoneNumber object = new KeyAndPhoneNumber(); - object.setVal(new PhoneNumber("206", "266", "1000")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testPhoneNumberAreaCodeNull() { - final KeyAndPhoneNumber object = new KeyAndPhoneNumber(); - object.setVal(new PhoneNumber(null, "266", "1000")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testPhoneNumberAreaCodeEmpty() { - final KeyAndPhoneNumber object = new KeyAndPhoneNumber(); - object.setVal(new PhoneNumber("", "266", "1000")); - - final PhoneNumber after = assertBeforeAndAfterChange(null, object); - assertNotNull(after); - assertNull(after.getAreaCode()); - assertEquals(object.getVal().getExchange(), after.getExchange()); - assertEquals(object.getVal().getSubscriber(), after.getSubscriber()); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testPhoneNumberNull() { - final KeyAndPhoneNumber object = new KeyAndPhoneNumber(); - object.setVal(null); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testPhoneNumberAllNull() { - final KeyAndPhoneNumber object = new KeyAndPhoneNumber(); - object.setVal(new PhoneNumber(null, null, null)); - - final PhoneNumber after = assertBeforeAndAfterChange(null, object); - assertNull(after); //<- and empty object produces a null val - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testKeyAndSpecialCharacterDelimitedierPhoneNumber() { - final KeyAndSpecialCharacterDelimitedierPhoneNumber object = new KeyAndSpecialCharacterDelimitedierPhoneNumber(); - object.setVal(new PhoneNumber("206", "867", "5309")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testKeyAndDefaultDelimitederPhoneNumber() { - final KeyAndDefaultDelimitederPhoneNumber object = new KeyAndDefaultDelimitederPhoneNumber(); - object.setVal(new PhoneNumber("206", "867", "5309")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test - public void testPhoneNumberDelimitedOnType() { - final KeyAndPhoneNumberDelimitedOnType object = new KeyAndPhoneNumberDelimitedOnType(); - object.setVal(new KeyAndPhoneNumberDelimitedOnType.Value()); - object.getVal().setAreaCode("206"); - object.getVal().setExchange("266"); - object.getVal().setSubscriber("1000"); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test(expected = DynamoDbMappingException.class) - public void testKeyAndNoAttributeNamesPhoneNumber() { - final KeyAndNoAttributeNamesPhoneNumber object = new KeyAndNoAttributeNamesPhoneNumber(); - object.setVal(new PhoneNumber("206", "867", "5309")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code PhoneNumber}. - */ - @Test(expected = DynamoDbMappingException.class) - public void testExceptionOnPhoneNumberSet() { - final KeyAndPhoneNumberSet object = new KeyAndPhoneNumberSet(); - object.setVal(Collections.singleton(new PhoneNumber("206", "266", "1000"))); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testFormatDateRange() throws Exception { - final KeyAndFormatDateRange object = new KeyAndFormatDateRange(); - object.setVal(new KeyAndFormatDateRange.FormatDateRange()); - object.getVal().setStart(new Date(System.currentTimeMillis() - 6000L)); - object.getVal().setEnd(new Date(System.currentTimeMillis() + 6000L)); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testFormatDateRangeStartNull() throws Exception { - final KeyAndFormatDateRange object = new KeyAndFormatDateRange(); - object.setVal(new KeyAndFormatDateRange.FormatDateRange()); - object.getVal().setStart(null); - object.getVal().setEnd(new Date()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testFormatDateRangeEndNull() throws Exception { - final KeyAndFormatDateRange object = new KeyAndFormatDateRange(); - object.setVal(new KeyAndFormatDateRange.FormatDateRange()); - object.getVal().setStart(new Date()); - object.getVal().setEnd(null); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testFormatDateRangeStartAndEndNull() { - final KeyAndFormatDateRange object = new KeyAndFormatDateRange(); - object.setVal(new KeyAndFormatDateRange.FormatDateRange()); - object.getVal().setStart(null); - object.getVal().setEnd(null); - - final KeyAndFormatDateRange.FormatDateRange after = assertBeforeAndAfterChange(null, object); - assertNull(after); //<- and empty object produces a null val - } - - /** - * Test using {@code DateRange}. - */ - @Test - @Ignore("Behavior is different with java.time; cannot parse 'yyyMMdd' formatted date to ZonedDateTime.") - public void testDelimitedKeyAndDate() { - final DelimitedKeyAndDate object = new DelimitedKeyAndDate(); - object.setKey(new DelimitedKeyAndDate.Key()); - object.getKey().setKey(UUID.randomUUID().toString()); - object.getKey().setVal(new Date()); - assertBeforeAndAfterChange(true, object); - } - - /** - * Test marshalling. - */ - @Test - public void testKeyAndCurrency() { - final KeyAndCurrency object = new KeyAndCurrency(); - object.setVal(new Currency(12.95D, "USD")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test marshalling. - */ - @Test(expected = DynamoDbMappingException.class) - public void testKeyAndNestedCurrency() { - final KeyAndNestedCurrency object = new KeyAndNestedCurrency(); - object.setVal(new KeyAndNestedCurrency.Value()); - object.getVal().setKey(UUID.randomUUID().toString()); - object.getVal().setVal(new Currency(12.95D, "USD")); - assertBeforeAndAfterChange(false, object); - } - - /** - * An object with {@code PhoneNumber}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndSpecialCharacterDelimitedierPhoneNumber extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"areaCode", "exchange", "subscriber"}, delimiter = '\\') - public PhoneNumber getVal() { - return super.getVal(); - } - - @Override - public void setVal(final PhoneNumber val) { - super.setVal(val); - } - } - - /** - * An object with {@code PhoneNumber}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDefaultDelimitederPhoneNumber extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"areaCode", "exchange", "subscriber"}) - public PhoneNumber getVal() { - return super.getVal(); - } - - @Override - public void setVal(final PhoneNumber val) { - super.setVal(val); - } - } - - /** - * An object with {@code PhoneNumber}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndPhoneNumber extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"areaCode", "exchange", "subscriber"}, delimiter = '-') - public PhoneNumber getVal() { - return super.getVal(); - } - - @Override - public void setVal(final PhoneNumber val) { - super.setVal(val); - } - } - - /** - * An object with {@code PhoneNumber}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndPhoneNumberDelimitedOnType extends AutoKeyAndVal { - public Value getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Value val) { - super.setVal(val); - } - - @DynamoDbDelimited(attributeNames = {"areaCode", "exchange", "subscriber"}, delimiter = '.') - public static class Value extends PhoneNumber { - } - } - - /** - * An object with {@code PhoneNumber}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndNoAttributeNamesPhoneNumber extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {}) - public PhoneNumber getVal() { - return super.getVal(); - } - - @Override - public void setVal(final PhoneNumber val) { - super.setVal(val); - } - } - - /** - * An object with {@code PhoneNumber}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndPhoneNumberSet extends AutoKeyAndVal> { - @DynamoDbDelimited(attributeNames = {"areaCode", "exchange", "subscriber"}, delimiter = '-') - public Set getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Set val) { - super.setVal(val); - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateRange extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"start", "end"}) - public DateRange getVal() { - return super.getVal(); - } - - @Override - public void setVal(final DateRange val) { - super.setVal(val); - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndFormatDateRange extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"start", "end"}) - public FormatDateRange getVal() { - return super.getVal(); - } - - @Override - public void setVal(final FormatDateRange val) { - super.setVal(val); - } - - public static class FormatDateRange extends DateRange { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSS") - public Date getStart() { - return super.getStart(); - } - - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSS") - public Date getEnd() { - return super.getEnd(); - } - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class DelimitedKeyAndDate extends KeyAndVal { - @DynamoDbHashKey - @DynamoDbDelimited(attributeNames = {"key", "val"}) - public DelimitedKeyAndDate.Key getKey() { - return super.getKey(); - } - - @Override - public void setKey(final DelimitedKeyAndDate.Key key) { - super.setKey(key); - } - - @DynamoDbAutoGeneratedTimestamp - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - - public static class Key extends KeyAndVal { - @Override - public String getKey() { - return super.getKey(); - } - - @Override - public void setKey(final String key) { - super.setKey(key); - } - - @DynamoDbTypeConvertedTimestamp(pattern = "yyyyMMdd") - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - } - } - - /** - * An object with a complex type. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCurrency extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"amount", "unit"}, delimiter = '$') - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - } - - /** - * An object with a complex type. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndNestedCurrency extends AutoKeyAndVal { - @DynamoDbDelimited(attributeNames = {"key", "val"}, delimiter = '#') - public KeyAndNestedCurrency.Value getVal() { - return super.getVal(); - } - - @Override - public void setVal(final KeyAndNestedCurrency.Value val) { - super.setVal(val); - } - - public static class Value extends KeyAndVal { - @Override - public String getKey() { - return super.getKey(); - } - - @Override - public void setKey(final String key) { - super.setKey(key); - } - - @Override - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ExceptionHandlingIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ExceptionHandlingIntegrationTest.java deleted file mode 100644 index 08548ff85f98..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ExceptionHandlingIntegrationTest.java +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchemas; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbVersionAttribute; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; - -/** - * Tests of exception handling - */ -public class ExceptionHandlingIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - @Test(expected = DynamoDbMappingException.class) - public void testNoTableAnnotation() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(new NoTableAnnotation()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testNoTableAnnotationLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.load(NoTableAnnotation.class, "abc"); - } - - @Test(expected = DynamoDbMappingException.class) - public void testNoDefaultConstructor() { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - NoDefaultConstructor obj = new NoDefaultConstructor("" + startKey++, "abc"); - util.save(obj); - util.load(NoDefaultConstructor.class, obj.getKey()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testNoHashKeyGetter() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(new NoKeyGetterDefined()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testNoHashKeyGetterLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.load(NoKeyGetterDefined.class, "abc"); - } - - @Test(expected = DynamoDbMappingException.class) - public void testPrivateKeyGetter() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(new PrivateKeyGetter()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testPrivateKeyGetterLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.load(PrivateKeyGetter.class, "abc"); - } - - @Test(expected = DynamoDbMappingException.class) - public void testPrivateKeySetter() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(new PrivateKeySetter()); - } - - /* - * To trigger this error, we need for a service object to be present, so - * we'll insert one manually. - */ - @Test(expected = DynamoDbMappingException.class) - public void testPrivateKeySetterLoad() throws Exception { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().s("abc").build()); - dynamo.putItem(PutItemRequest.builder().tableName("aws-java-sdk-util").item(attr).build()); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.load(PrivateKeySetter.class, "abc"); - } - - @Test(expected = DynamoDbMappingException.class) - public void testPrivateSetterLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - PrivateSetter object = new PrivateSetter(); - object.setStringProperty("value"); - util.save(object); - util.load(PrivateSetter.class, object.getKey()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testOverloadedSetter() { - OverloadedSetter obj = new OverloadedSetter(); - obj.setKey("" + startKey++); - obj.setAttribute("abc", "123"); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - - mapper.load(OverloadedSetter.class, obj.getKey()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testWrongTypeForSetter() { - WrongTypeForSetter obj = new WrongTypeForSetter(); - obj.setKey("" + startKey++); - obj.setAttribute(123); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - - mapper.load(WrongTypeForSetter.class, obj.getKey()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testWrongDataType() { - Map attr = new HashMap(); - attr.put("integerProperty", AttributeValue.builder().s("abc").build()); - attr.put(KEY_NAME, AttributeValue.builder().s("" + startKey++).build()); - dynamo.putItem(PutItemRequest.builder().tableName("aws-java-sdk-util").item(attr).build()); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.load(NumericFields.class, attr.get(KEY_NAME).s()); - } - - @Test(expected = DynamoDbMappingException.class) - public void testWrongDataType2() { - Map attr = new HashMap(); - attr.put("integerProperty", AttributeValue.builder().ns("1", "2", "3").build()); - attr.put(KEY_NAME, AttributeValue.builder().s("" + startKey++).build()); - dynamo.putItem(PutItemRequest.builder().tableName("aws-java-sdk-util").item(attr).build()); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.load(NumericFields.class, attr.get(KEY_NAME).s()); - } - - // Complex types are not supported by the V1 conversion schema - @Test(expected = DynamoDbMappingException.class) - public void testComplexTypeFailure() { - DynamoDbMapperConfig config = new DynamoDbMapperConfig(ConversionSchemas.V1); - DynamoDbMapper util = new DynamoDbMapper(dynamo, config); - - ComplexType complexType = new ComplexType("" + startKey++, new ComplexType("" + startKey++, null)); - util.save(complexType); - } - - @Test(expected = DynamoDbMappingException.class) - public void testUnsupportedHashKeyType() { - ComplexType complexType = new ComplexType("" + startKey++, new ComplexType("" + startKey++, null)); - ComplexHashKeyType obj = new ComplexHashKeyType(); - obj.setKey(complexType); - obj.setAttribute("abc"); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(obj); - } - - // Lists are not supported by the V1 conversion schema. - @Test(expected = DynamoDbMappingException.class) - public void testNonsetCollection() { - DynamoDbMapperConfig config = new DynamoDbMapperConfig(ConversionSchemas.V1); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo, config); - - NonSetCollectionType obj = new NonSetCollectionType(); - obj.setKey("" + startKey++); - obj.setBadlyMapped(new ArrayList()); - obj.getBadlyMapped().add("abc"); - mapper.save(obj); - } - - @Test(expected = DynamoDbMappingException.class) - public void testFractionalVersionAttribute() { - FractionalVersionAttribute obj = new FractionalVersionAttribute(); - obj.setKey("" + startKey++); - obj.setVersion(0d); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - } - - @Test(expected = DynamoDbMappingException.class) - public void testAutoGeneratedIntegerHashKey() { - AutoGeneratedIntegerKey obj = new AutoGeneratedIntegerKey(); - obj.setValue("fdgfdsgf"); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - } - - @Test(expected = DynamoDbMappingException.class) - public void testAutoGeneratedIntegerRangeKey() { - AutoGeneratedIntegerRangeKey obj = new AutoGeneratedIntegerRangeKey(); - obj.setKey("Bldadsfa"); - obj.setValue("fdgfdsgf"); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - } - - public static class NoTableAnnotation { - - private String key; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class NoDefaultConstructor { - - private String key; - private String attribute; - - public NoDefaultConstructor(String key, String attribute) { - super(); - this.key = key; - this.attribute = attribute; - } - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getAttribute() { - return attribute; - } - - public void setAttribute(String attribute) { - this.attribute = attribute; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class NoKeyGetterDefined { - - @SuppressWarnings("unused") - private String key; - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class PrivateKeyGetter { - - private String key; - - @SuppressWarnings("unused") - @DynamoDbHashKey - private String getKey() { - return key; - } - - @SuppressWarnings("unused") - private void setKey(String key) { - this.key = key; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class PrivateKeySetter { - - private String key; - - @DynamoDbHashKey - @DynamoDbAutoGeneratedKey - public String getKey() { - return key; - } - - @SuppressWarnings("unused") - private void setKey(String key) { - this.key = key; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class PrivateSetter { - - private String key; - private String stringProperty; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String stringProperty() { - return stringProperty; - } - - private void setStringProperty(String stringProperty) { - this.stringProperty = stringProperty; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class OverloadedSetter { - - private String key; - private String attribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getAttribute() { - return attribute; - } - - public void setAttribute(String attribute, String unused) { - this.attribute = attribute; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class WrongTypeForSetter { - - private String key; - private String attribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getAttribute() { - return attribute; - } - - public void setAttribute(Integer attribute) { - this.attribute = String.valueOf(attribute); - } - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class NumericFields { - - private String key; - private Integer integerProperty; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public Integer getIntegerProperty() { - return integerProperty; - } - - public void setIntegerProperty(Integer integerProperty) { - this.integerProperty = integerProperty; - } - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class ComplexType { - - public String key; - public ComplexType type; - - public ComplexType(String key, ComplexType type) { - super(); - this.key = key; - this.type = type; - } - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public ComplexType getType() { - return type; - } - - public void setType(ComplexType type) { - this.type = type; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class ComplexHashKeyType { - - private ComplexType key; - private String attribute; - - @DynamoDbHashKey - public ComplexType getKey() { - return key; - } - - public void setKey(ComplexType key) { - this.key = key; - } - - public String getAttribute() { - return attribute; - } - - public void setAttribute(String attribute) { - this.attribute = attribute; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class NonSetCollectionType { - - private String key; - private List badlyMapped; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public List getBadlyMapped() { - return badlyMapped; - } - - public void setBadlyMapped(List badlyMapped) { - this.badlyMapped = badlyMapped; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class FractionalVersionAttribute { - - private String key; - private Double version; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbVersionAttribute - public Double getVersion() { - return version; - } - - public void setVersion(Double version) { - this.version = version; - } - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class AutoGeneratedIntegerKey { - - private Integer key; - private String value; - - @DynamoDbHashKey - @DynamoDbAutoGeneratedKey - public Integer getKey() { - return key; - } - - public void setKey(Integer key) { - this.key = key; - } - - public String value() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class AutoGeneratedIntegerRangeKey { - - private String key; - private Integer rangekey; - private String value; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAutoGeneratedKey - @DynamoDbRangeKey - public Integer getRangekey() { - return rangekey; - } - - public void setRangekey(Integer rangekey) { - this.rangekey = rangekey; - } - - public String value() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/FlattenedIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/FlattenedIntegrationTest.java deleted file mode 100644 index 3779b1358886..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/FlattenedIntegrationTest.java +++ /dev/null @@ -1,317 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Set; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGenerateStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedTimestamp; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbFlattened; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.pojos.AuditRecord; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.Currency; -import software.amazon.awssdk.services.dynamodb.pojos.DateRange; - -/** - * Tests updating component attribute fields correctly. - */ -public class FlattenedIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test the mappings. - */ - @Test - public void testAuditRecord() { - final KeyAndAuditRecord object = new KeyAndAuditRecord(); - final AuditRecord auditRecord = assertBeforeAndAfterChange(true, object); - assertNotNull(auditRecord.getCreatedDate()); - assertNotNull(auditRecord.getLastModifiedDate()); - assertEquals(Long.valueOf(1L), auditRecord.getVersionNumber()); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testDateRange() throws Exception { - final KeyAndDateRange object = new KeyAndDateRange(); - object.setVal(new DateRange(new Date(), -60000L, 60000L)); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testDateRangeIsNull() { - final KeyAndDateRange object = new KeyAndDateRange(); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testDateRangeStartAndEndIsNull() { - final KeyAndDateRange object = new KeyAndDateRange(); - object.setVal(new DateRange()); - - final DateRange after = assertBeforeAndAfterChange(null, object); - assertNull(after); //<- an empty date range produces null object - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testDateRangeStartIsNull() throws Exception { - final KeyAndDateRange object = new KeyAndDateRange(); - object.setVal(new DateRange(null, new Date())); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testDateRangeEndIsNull() throws Exception { - final KeyAndDateRange object = new KeyAndDateRange(); - object.setVal(new DateRange(new Date(), null)); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test - public void testAutoDateRange() throws Exception { - final KeyAndAutoDateRange object = new KeyAndAutoDateRange(); - - assertBeforeAndAfterChange(true, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test(expected = DynamoDbMappingException.class) - public void testKeyAndUnknownAttribute() { - final KeyAndUnknownAttribute object = new KeyAndUnknownAttribute(); - object.setVal(new DateRange(new Date(), -60000L, 60000L)); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test(expected = DynamoDbMappingException.class) - public void testDateRangeSet() { - final KeyAndDateRangeSet object = new KeyAndDateRangeSet(); - object.setVal(Collections.singleton(new DateRange(new Date(), -60000L, 60000L))); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code DateRange}. - */ - @Test(expected = DynamoDbMappingException.class) - public void testDateRangeList() { - final KeyAndDateRangeList object = new KeyAndDateRangeList(); - object.setVal(Collections.singletonList(new DateRange(new Date(), -60000L, 60000L))); - - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testKeyAndCurrencies() { - final KeyAndCurrencies object = new KeyAndCurrencies(); - object.setVal(new Currency(1000000D, "CAD")); - object.setOther(new Currency(99.99D, "USD")); - - assertBeforeAndAfterChange(false, object); - - final KeyAndCurrencies reload = util.load(object.getClass(), object.getKey()); - assertEquals(object.getVal(), reload.getVal()); - assertEquals(object.getOther(), reload.getOther()); - } - - /** - * test object. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndAuditRecord extends AutoKeyAndVal { - public AuditRecord getVal() { - return super.getVal(); - } - - @Override - public void setVal(final AuditRecord val) { - super.setVal(val); - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateRange extends AutoKeyAndVal { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "start", attributeName = "DateRangeStart"), - @DynamoDbAttribute(mappedBy = "end", attributeName = "DateRangeEnd")}) - public DateRange getVal() { - return super.getVal(); - } - - @Override - public void setVal(final DateRange val) { - super.setVal(val); - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndAutoDateRange extends AutoKeyAndVal { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "start", attributeName = "CreatedDate"), - @DynamoDbAttribute(mappedBy = "end", attributeName = "LastModifiedDate")}) - public AutoDateRange getVal() { - return super.getVal(); - } - - @Override - public void setVal(final AutoDateRange val) { - super.setVal(val); - } - - public static class AutoDateRange extends DateRange { - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.CREATE) - public Date start() { - return super.getStart(); - } - - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.ALWAYS) - public Date getEnd() { - return super.getEnd(); - } - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndUnknownAttribute extends AutoKeyAndVal { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "xstart", attributeName = "DateRangeStart"), - @DynamoDbAttribute(mappedBy = "end", attributeName = "DateRangeEnd")}) - public DateRange getVal() { - return super.getVal(); - } - - @Override - public void setVal(final DateRange val) { - super.setVal(val); - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateRangeSet extends AutoKeyAndVal> { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "start", attributeName = "starts"), - @DynamoDbAttribute(mappedBy = "end", attributeName = "ends")}) - public Set getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Set val) { - super.setVal(val); - } - } - - /** - * An object with {@code DateRange}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateRangeList extends AutoKeyAndVal> { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "start", attributeName = "starts"), - @DynamoDbAttribute(mappedBy = "end", attributeName = "ends")}) - public List getVal() { - return super.getVal(); - } - - @Override - public void setVal(final List val) { - super.setVal(val); - } - } - - /** - * An object with {@code Currency}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCurrencies extends AutoKeyAndVal { - private Currency other; - - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "amount", attributeName = "firstAmount"), - @DynamoDbAttribute(mappedBy = "unit", attributeName = "firstUnit")}) - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "amount", attributeName = "secondAmount"), - @DynamoDbAttribute(mappedBy = "unit", attributeName = "secondUnit")}) - public Currency getOther() { - return this.other; - } - - public void setOther(final Currency other) { - this.other = other; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/GenerateCreateTableRequestIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/GenerateCreateTableRequestIntegrationTest.java deleted file mode 100644 index 85c3589763ff..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/GenerateCreateTableRequestIntegrationTest.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.ImmutableObjectUtils; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexDescription; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndexDescription; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.testutils.UnorderedCollectionComparator; -import utils.test.util.DynamoDBTestBase; - -/** - * Tests that the CreateTableRequest generated by DynamoDBMapper.generateCreateTableRequest - * correctly creates the expected table. - */ -public class GenerateCreateTableRequestIntegrationTest extends DynamoDBTestBase { - - private static final ProvisionedThroughput DEFAULT_CAPACITY = ProvisionedThroughput.builder().readCapacityUnits(5L).writeCapacityUnits(5L).build(); - private static DynamoDbMapper mapper; - private static Set testedTableName = new HashSet<>(); - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBTestBase.setUpTestBase(); - mapper = new DynamoDbMapper(dynamo); - } - - @AfterClass - public static void tearDown() { - for (String tableName : testedTableName) { - dynamo.deleteTable(DeleteTableRequest.builder().tableName(tableName).build()); - } - } - - private static void setProvisionedThroughput(CreateTableRequest request, ProvisionedThroughput throughput) { - ImmutableObjectUtils.setObjectMember(request, "provisionedThroughput", throughput); - //request.setProvisionedThroughput(throughput); - if (request.globalSecondaryIndexes() != null) { - for (GlobalSecondaryIndex gsi : request.globalSecondaryIndexes()) { - ImmutableObjectUtils.setObjectMember(gsi, "provisionedThroughput", throughput); - //gsi.setProvisionedThroughput(throughput); - } - } - } - - private static boolean equalLsi(Collection a, Collection b) { - return UnorderedCollectionComparator.equalUnorderedCollections(a, b, new LocalSecondaryIndexDefinitionComparator()); - } - - private static boolean equalGsi(Collection a, Collection b) { - return UnorderedCollectionComparator.equalUnorderedCollections(a, b, new GlobalSecondaryIndexDefinitionComparator()); - } - - private static String appendCurrentTimeToTableName(CreateTableRequest request) { - String appendedName = String.format("%s-%d", request.tableName(), System.currentTimeMillis()); - ImmutableObjectUtils.setObjectMember(request, "tableName", appendedName); - /// /request.setTableName(appendedName); - return appendedName; - } - - @Test - public void testParseIndexRangeKeyClass() throws Exception { - CreateTableRequest request = mapper.generateCreateTableRequest(IndexRangeKeyClass.class); - String createdTableName = appendCurrentTimeToTableName(request); - testedTableName.add(createdTableName); - setProvisionedThroughput(request, DEFAULT_CAPACITY); - - TableDescription createdTableDescription = dynamo.createTable(request).tableDescription(); - - assertEquals(createdTableName, createdTableDescription.tableName()); - List expectedKeyElements = Arrays.asList( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("rangeKey").keyType(KeyType.RANGE).build() - ); - assertEquals(expectedKeyElements, createdTableDescription.keySchema()); - - List expectedAttrDefinitions = Arrays.asList( - AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("rangeKey").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("indexFooRangeKey").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("indexBarRangeKey").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("multipleIndexRangeKey").attributeType(ScalarAttributeType.N).build() - ); - assertTrue(UnorderedCollectionComparator.equalUnorderedCollections( - expectedAttrDefinitions, - createdTableDescription.attributeDefinitions())); - - List expectedLsi = Arrays.asList( - LocalSecondaryIndex.builder() - .indexName("index_foo") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexFooRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("index_bar") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexBarRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("index_foo_copy") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("multipleIndexRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("index_bar_copy") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("multipleIndexRangeKey").keyType(KeyType.RANGE).build()).build()); - assertTrue(equalLsi(expectedLsi, createdTableDescription.localSecondaryIndexes())); - - assertNull(request.globalSecondaryIndexes()); - assertEquals(DEFAULT_CAPACITY, request.provisionedThroughput()); - - // Only one table with indexes can be created simultaneously - TableUtils.waitUntilActive(dynamo, createdTableName); - } - - @Test - public void testComplexIndexedHashRangeClass() throws Exception { - CreateTableRequest request = mapper.generateCreateTableRequest(MapperQueryExpressionTest.HashRangeClass.class); - String createdTableName = appendCurrentTimeToTableName(request); - testedTableName.add(createdTableName); - setProvisionedThroughput(request, DEFAULT_CAPACITY); - - TableDescription createdTableDescription = dynamo.createTable(request).tableDescription(); - - assertEquals(createdTableName, createdTableDescription.tableName()); - List expectedKeyElements = Arrays.asList( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("primaryRangeKey").keyType(KeyType.RANGE).build() - ); - assertEquals(expectedKeyElements, createdTableDescription.keySchema()); - - List expectedAttrDefinitions = Arrays.asList( - AttributeDefinition.builder().attributeName("primaryHashKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("indexHashKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("primaryRangeKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("indexRangeKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("anotherIndexRangeKey").attributeType(ScalarAttributeType.S).build() - ); - assertTrue(UnorderedCollectionComparator.equalUnorderedCollections( - expectedAttrDefinitions, - createdTableDescription.attributeDefinitions())); - - List expectedLsi = Arrays.asList( - LocalSecondaryIndex.builder() - .indexName("LSI-primary-range") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("primaryRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("LSI-index-range-1") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("LSI-index-range-2") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("LSI-index-range-3") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("anotherIndexRangeKey").keyType(KeyType.RANGE).build()).build()); - assertTrue(equalLsi(expectedLsi, createdTableDescription.localSecondaryIndexes())); - - List expectedGsi = Arrays.asList( - GlobalSecondaryIndex.builder() - .indexName("GSI-primary-hash-index-range-1") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-primary-hash-index-range-2") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("anotherIndexRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-index-hash-primary-range") - .keySchema( - KeySchemaElement.builder().attributeName("indexHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("primaryRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-index-hash-index-range-1") - .keySchema( - KeySchemaElement.builder().attributeName("indexHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-index-hash-index-range-2") - .keySchema( - KeySchemaElement.builder().attributeName("indexHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build()); - assertTrue(equalGsi(expectedGsi, createdTableDescription.globalSecondaryIndexes())); - - assertEquals(DEFAULT_CAPACITY, request.provisionedThroughput()); - - // Only one table with indexes can be created simultaneously - TableUtils.waitUntilActive(dynamo, createdTableName); - } - - private static class LocalSecondaryIndexDefinitionComparator - implements - UnorderedCollectionComparator.CrossTypeComparator { - - @Override - public boolean equals(LocalSecondaryIndex a, LocalSecondaryIndexDescription b) { - return a.indexName().equals(b.indexName()) - && a.keySchema().equals(b.keySchema()); - } - - } - - private static class GlobalSecondaryIndexDefinitionComparator - implements - UnorderedCollectionComparator.CrossTypeComparator { - - @Override - public boolean equals(GlobalSecondaryIndex a, GlobalSecondaryIndexDescription b) { - return a.indexName().equals(b.indexName()) - && a.keySchema().equals(b.keySchema()); - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/HashKeyOnlyTableWithGSIIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/HashKeyOnlyTableWithGSIIntegrationTest.java deleted file mode 100644 index 07c67da56270..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/HashKeyOnlyTableWithGSIIntegrationTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.PaginatedQueryList; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.Projection; -import software.amazon.awssdk.services.dynamodb.model.ProjectionType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import utils.test.util.DynamoDBTestBase; - -/** - * Integration test for GSI support with a table that has no primary range key (only a primary hash key). - */ -public class HashKeyOnlyTableWithGSIIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - public static final String HASH_KEY_ONLY_TABLE_NAME = "no-primary-range-key-gsi-test"; - - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBTestBase.setUpTestBase(); - List keySchema = new ArrayList(); - keySchema.add(KeySchemaElement.builder().attributeName("id").keyType(KeyType.HASH).build()); - - CreateTableRequest req = CreateTableRequest.builder() - .tableName(HASH_KEY_ONLY_TABLE_NAME) - .keySchema(keySchema) - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName("id").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("status").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("ts").attributeType(ScalarAttributeType.S).build()) - .globalSecondaryIndexes( - GlobalSecondaryIndex.builder() - .provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build()) - .indexName("statusAndCreation") - .keySchema( - KeySchemaElement.builder().attributeName("status").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("ts").keyType(KeyType.RANGE).build()) - .projection( - Projection.builder().projectionType(ProjectionType.ALL).build()).build()).build(); - - TableUtils.createTableIfNotExists(dynamo, req); - TableUtils.waitUntilActive(dynamo, HASH_KEY_ONLY_TABLE_NAME); - } - - @AfterClass - public static void tearDown() throws Exception { - dynamo.deleteTable(DeleteTableRequest.builder().tableName(HASH_KEY_ONLY_TABLE_NAME).build()); - } - - /** - * Tests that we can query using the hash/range GSI on our hash-key only table. - */ - @Test - public void testGSIQuery() throws Exception { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - String status = "foo-status"; - - User user = new User(); - user.setId("123"); - user.setStatus(status); - user.setTs("321"); - mapper.save(user); - - DynamoDbQueryExpression expr = new DynamoDbQueryExpression() - .withIndexName("statusAndCreation") - .withLimit(100) - .withConsistentRead(false) - .withHashKeyValues(user) - .withRangeKeyCondition("ts", - Condition.builder() - .comparisonOperator(ComparisonOperator.GT) - .attributeValueList(AttributeValue.builder().s("100").build()).build()); - - PaginatedQueryList query = mapper.query(User.class, expr); - assertEquals(1, query.size()); - assertEquals(status, query.get(0).getStatus()); - } - - @DynamoDbTable(tableName = HASH_KEY_ONLY_TABLE_NAME) - public static class User { - private String id; - private String status; - private String ts; - - @DynamoDbHashKey - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - @DynamoDbIndexHashKey(globalSecondaryIndexName = "statusAndCreation") - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - @DynamoDbIndexRangeKey(globalSecondaryIndexName = "statusAndCreation") - public String getTs() { - return ts; - } - - public void setTs(String ts) { - this.ts = ts; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/IndexRangeKeyAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/IndexRangeKeyAttributesIntegrationTest.java deleted file mode 100644 index 7733f9d575bd..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/IndexRangeKeyAttributesIntegrationTest.java +++ /dev/null @@ -1,439 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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. - */ - -/* - * - * Licensed 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://aws.amazon.com/apache2.0 - * - * This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.ConsistentRead; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; - -/** - * Tests that index range keys are properly handled as common attribute - * when items are loaded, saved/updated by using primary key. - * Also tests using index range keys for queries. - */ -public class IndexRangeKeyAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String RANGE_KEY = "rangeKey"; - private static final String INDEX_FOO_RANGE_KEY = "indexFooRangeKey"; - private static final String INDEX_BAR_RANGE_KEY = "indexBarRangeKey"; - private static final String MULTIPLE_INDEX_RANGE_KEY = "multipleIndexRangeKey"; - private static final String FOO_ATTRIBUTE = "fooAttribute"; - private static final String BAR_ATTRIBUTE = "barAttribute"; - private static final String VERSION_ATTRIBUTE = "version"; - private static final List> attrs = new LinkedList>(); - private static final List hashKeyValues = new LinkedList(); - private static final int totalHash = 5; - private static final int rangePerHash = 64; - private static final int indexFooRangeStep = 2; - private static final int indexBarRangeStep = 4; - private static final int multipleIndexRangeStep = 8; - private static DynamoDbMapper mapper; - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - - // Test data - static { - for (int i = 0; i < totalHash; i++) { - long hashKeyValue = startKey++; - hashKeyValues.add(hashKeyValue); - for (int j = 0; j < rangePerHash; j++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().n("" + hashKeyValue).build()); - attr.put(RANGE_KEY, AttributeValue.builder().n("" + j).build()); - if (j % indexFooRangeStep == 0) { - attr.put(INDEX_FOO_RANGE_KEY, AttributeValue.builder().n("" + j).build()); - } - if (j % indexBarRangeStep == 0) { - attr.put(INDEX_BAR_RANGE_KEY, AttributeValue.builder().n("" + j).build()); - } - if (j % multipleIndexRangeStep == 0) { - attr.put(MULTIPLE_INDEX_RANGE_KEY, AttributeValue.builder().n("" + j).build()); - } - attr.put(FOO_ATTRIBUTE, AttributeValue.builder().s(UUID.randomUUID().toString()).build()); - attr.put(BAR_ATTRIBUTE, AttributeValue.builder().s(UUID.randomUUID().toString()).build()); - attr.put(VERSION_ATTRIBUTE, AttributeValue.builder().n("1").build()); - - attrs.add(attr); - } - } - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - boolean recreateTable = false; - setUpTableWithIndexRangeAttribute(recreateTable); - - // Insert the data - for (Map attr : attrs) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE).item(attr).build()); - } - - mapper = new DynamoDbMapper(dynamo, - new DynamoDbMapperConfig(ConsistentRead.CONSISTENT)); - } - - /** - * Tests that attribute annotated with @DynamoDBIndexRangeKey is properly set in the loaded object. - */ - @Test - public void testLoad() throws Exception { - for (Map attr : attrs) { - IndexRangeKeyClass x = mapper.load(newIndexRangeKey(Long.parseLong(attr.get(KEY_NAME).n()), - Double.parseDouble(attr.get(RANGE_KEY).n()))); - - // Convert all numbers to the most inclusive type for easy - // comparison - assertEquals(new BigDecimal(x.getKey()), new BigDecimal(attr.get(KEY_NAME).n())); - assertEquals(new BigDecimal(x.getRangeKey()), new BigDecimal(attr.get(RANGE_KEY).n())); - if (null == attr.get(INDEX_FOO_RANGE_KEY)) { - assertNull(x.getIndexFooRangeKeyWithFakeName()); - } else { - assertEquals(new BigDecimal(x.getIndexFooRangeKeyWithFakeName()), - new BigDecimal(attr.get(INDEX_FOO_RANGE_KEY).n())); - } - if (null == attr.get(INDEX_BAR_RANGE_KEY)) { - assertNull(x.getIndexBarRangeKey()); - } else { - assertEquals(new BigDecimal(x.getIndexBarRangeKey()), new BigDecimal(attr.get(INDEX_BAR_RANGE_KEY).n())); - } - assertEquals(new BigDecimal(x.getVersion()), new BigDecimal(attr.get(VERSION_ATTRIBUTE).n())); - assertEquals(x.getFooAttribute(), attr.get(FOO_ATTRIBUTE).s()); - assertEquals(x.getBarAttribute(), attr.get(BAR_ATTRIBUTE).s()); - - } - } - - private IndexRangeKeyClass newIndexRangeKey(long hashKey, double rangeKey) { - IndexRangeKeyClass obj = new IndexRangeKeyClass(); - obj.setKey(hashKey); - obj.setRangeKey(rangeKey); - return obj; - } - - /** - * Tests that attribute annotated with @DynamoDBIndexRangeKey is properly saved. - */ - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - IndexRangeKeyClass obj = getUniqueObject(); - objs.add(obj); - } - - for (IndexRangeKeyClass obj : objs) { - mapper.save(obj); - } - - for (IndexRangeKeyClass obj : objs) { - IndexRangeKeyClass loaded = mapper.load(IndexRangeKeyClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(obj, loaded); - } - } - - /** - * Tests that version attribute is still working as expected. - */ - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - IndexRangeKeyClass obj = getUniqueObject(); - objs.add(obj); - } - - for (IndexRangeKeyClass obj : objs) { - mapper.save(obj); - } - - for (IndexRangeKeyClass obj : objs) { - IndexRangeKeyClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - replacement.setRangeKey(obj.getRangeKey()); - replacement.setVersion(obj.getVersion()); - mapper.save(replacement); - - IndexRangeKeyClass loadedObject = mapper.load(IndexRangeKeyClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(replacement, loadedObject); - - // If we try to update the old version, we should get an error - replacement.setVersion(replacement.getVersion() - 1); - try { - mapper.save(replacement); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - } - } - - /** - * Tests making queries on local secondary index - */ - @Test - public void testQueryWithIndexRangekey() { - int indexFooRangePerHash = rangePerHash / indexFooRangeStep; - int indexBarRangePerHash = rangePerHash / indexBarRangeStep; - for (long hashKeyValue : hashKeyValues) { - IndexRangeKeyClass hashKeyItem = new IndexRangeKeyClass(); - hashKeyItem.setKey(hashKeyValue); - - /** - * Query items by primary range key - */ - List result = mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(RANGE_KEY, - Condition.builder() - .attributeValueList( - AttributeValue.builder() - .n("0").build()) - .comparisonOperator( - ComparisonOperator.GE - .toString()).build())); - assertTrue(rangePerHash == result.size()); - // check that all attributes are retrieved - for (IndexRangeKeyClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNotNull(itemInFooIndex.getBarAttribute()); - } - - /** - * Query items on index_foo - */ - result = mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(INDEX_FOO_RANGE_KEY, - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator( - ComparisonOperator.GE.toString()).build())); - assertTrue(indexFooRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNull(itemInFooIndex.getBarAttribute()); - } - - /** - * Query items on index_bar - */ - result = mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(INDEX_BAR_RANGE_KEY, - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator( - ComparisonOperator.GE.toString()).build())); - assertTrue(indexBarRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyClass itemInBarIndex : result) { - assertNull(itemInBarIndex.getFooAttribute()); - assertNotNull(itemInBarIndex.getBarAttribute()); - } - } - } - - /** - * Tests the exception when user specifies an invalid range key name in the query. - */ - @Test - public void testInvalidRangeKeyNameException() { - IndexRangeKeyClass hashKeyItem = new IndexRangeKeyClass(); - hashKeyItem.setKey(0); - try { - mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition("some_range_key", - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator(ComparisonOperator.GE.toString()).build())); - fail("some_range_key is not a valid range key name."); - } catch (DynamoDbMappingException e) { - System.out.println(e.getMessage()); - } catch (Exception e) { - fail("Should trigger an DynamoDBMappingException."); - } - } - - /** - * Tests the exception when user specifies an invalid index name in the query. - */ - @Test - public void testInvalidIndexNameException() { - IndexRangeKeyClass hashKeyItem = new IndexRangeKeyClass(); - hashKeyItem.setKey(0); - try { - mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(INDEX_BAR_RANGE_KEY, - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator(ComparisonOperator.GE.toString()).build()) - .withIndexName("some_index")); - fail("some_index is not a valid index name."); - } catch (IllegalArgumentException iae) { - System.out.println(iae.getMessage()); - } catch (Exception e) { - fail("Should trigger an IllegalArgumentException."); - } - } - - /** - * Tests making queries by using range key that is shared by multiple indexes. - */ - @Test - public void testQueryWithRangeKeyForMultipleIndexes() { - int multipleIndexRangePerHash = rangePerHash / multipleIndexRangeStep; - for (long hashKeyValue : hashKeyValues) { - IndexRangeKeyClass hashKeyItem = new IndexRangeKeyClass(); - hashKeyItem.setKey(hashKeyValue); - - /** - * Query items by a range key that is shared by multiple indexes - */ - List result = mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - Condition.builder() - .attributeValueList( - AttributeValue.builder() - .n("0").build()) - .comparisonOperator( - ComparisonOperator.GE - .toString()).build()) - .withIndexName("index_foo_copy")); - assertTrue(multipleIndexRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyClass itemInFooIndex : result) { - assertNotNull(itemInFooIndex.getFooAttribute()); - assertNull(itemInFooIndex.getBarAttribute()); - } - result = mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator( - ComparisonOperator.GE.toString()).build()) - .withIndexName("index_bar_copy")); - assertTrue(multipleIndexRangePerHash == result.size()); - // check that only the projected attributes are retrieved - for (IndexRangeKeyClass itemInFooIndex : result) { - assertNull(itemInFooIndex.getFooAttribute()); - assertNotNull(itemInFooIndex.getBarAttribute()); - } - - /** - * Exception when user doesn't specify which index to use - */ - try { - mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator(ComparisonOperator.GE.toString()).build())); - fail("No index name is specified when query with a range key shared by multiple indexes"); - } catch (IllegalArgumentException iae) { - System.out.println(iae.getMessage()); - } catch (Exception e) { - fail("Should trigger an IllegalArgumentException."); - } - - /** - * Exception when user uses an invalid index name - */ - try { - mapper.query(IndexRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyItem) - .withRangeKeyCondition(MULTIPLE_INDEX_RANGE_KEY, - Condition.builder() - .attributeValueList(AttributeValue.builder().n("0").build()) - .comparisonOperator(ComparisonOperator.GE.toString()).build()) - .withIndexName("index_foo")); - fail("index_foo is not annotated as part of the localSecondaryIndexNames in " + - "the @DynamoDBIndexRangeKey annotation of multipleIndexRangeKey"); - } catch (IllegalArgumentException iae) { - System.out.println(iae.getMessage()); - } catch (Exception e) { - fail("Should trigger an IllegalArgumentException."); - } - } - - } - - - private IndexRangeKeyClass getUniqueObject() { - IndexRangeKeyClass obj = new IndexRangeKeyClass(); - obj.setKey(startKey++); - obj.setRangeKey((double) start++); - obj.setIndexFooRangeKeyWithFakeName((double) start++); - obj.setIndexBarRangeKey((double) start++); - obj.setFooAttribute("" + startKey++); - obj.setBarAttribute("" + startKey++); - return obj; - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/InheritanceIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/InheritanceIntegrationTest.java deleted file mode 100644 index ec8097b12cf0..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/InheritanceIntegrationTest.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Tests inheritance behavior in DynamoDB mapper. - */ -public class InheritanceIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - @Test - public void testSubClass() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - SubClass obj = getUniqueObject(new SubClass()); - obj.setSubField("" + startKey++); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (Object obj : objs) { - util.save(obj); - assertEquals(util.load(SubClass.class, ((SubClass) obj).getKey()), obj); - } - } - - @Test - public void testSubsubClass() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - SubsubClass obj = getUniqueObject(new SubsubClass()); - obj.setSubField("" + startKey++); - obj.setSubsubField("" + startKey++); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (SubsubClass obj : objs) { - util.save(obj); - assertEquals(util.load(SubsubClass.class, obj.getKey()), obj); - } - } - - @Test(expected = DynamoDbMappingException.class) - public void testImplementation() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - Implementation obj = new Implementation(); - obj.setKey("" + startKey++); - obj.setAttribute("" + startKey++); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (Interface obj : objs) { - util.save(obj); - assertEquals(util.load(Implementation.class, obj.getKey()), obj); - } - } - - private T getUniqueObject(T obj) { - obj.setKey("" + startKey++); - obj.setNormalStringAttribute("" + startKey++); - return obj; - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static interface Interface { - - @DynamoDbHashKey - public String getKey(); - - public void setKey(String key); - - @DynamoDbAttribute - public String getAttribute(); - - public void setAttribute(String attribute); - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class BaseClass { - - protected String key; - protected String normalStringAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute - public String getNormalStringAttribute() { - return normalStringAttribute; - } - - public void setNormalStringAttribute(String normalStringAttribute) { - this.normalStringAttribute = normalStringAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - BaseClass other = (BaseClass) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (normalStringAttribute == null) { - if (other.normalStringAttribute != null) { - return false; - } - } else if (!normalStringAttribute.equals(other.normalStringAttribute)) { - return false; - } - return true; - } - } - - public static class SubClass extends BaseClass { - - private String subField; - - public String getSubField() { - return subField; - } - - public void setSubField(String subField) { - this.subField = subField; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((subField == null) ? 0 : subField.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - SubClass other = (SubClass) obj; - if (subField == null) { - if (other.subField != null) { - return false; - } - } else if (!subField.equals(other.subField)) { - return false; - } - return true; - } - - } - - public static class SubsubClass extends SubClass { - - private String subsubField; - - public String getSubsubField() { - return subsubField; - } - - public void setSubsubField(String subsubField) { - this.subsubField = subsubField; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((subsubField == null) ? 0 : subsubField.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - SubsubClass other = (SubsubClass) obj; - if (subsubField == null) { - if (other.subsubField != null) { - return false; - } - } else if (!subsubField.equals(other.subsubField)) { - return false; - } - return true; - } - } - - public static class Implementation implements Interface { - - private String key; - private String attribute; - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public String getAttribute() { - return attribute; - } - - public void setAttribute(String attribute) { - this.attribute = attribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((attribute == null) ? 0 : attribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Implementation other = (Implementation) obj; - if (attribute == null) { - if (other.attribute != null) { - return false; - } - } else if (!attribute.equals(other.attribute)) { - return false; - } - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - return true; - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/KeyOnlyPutIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/KeyOnlyPutIntegrationTest.java deleted file mode 100644 index 256670bfaaa3..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/KeyOnlyPutIntegrationTest.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbSaveExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import utils.test.util.DynamoDBIntegrationTestBase; - -public class KeyOnlyPutIntegrationTest extends DynamoDBIntegrationTestBase { - @Test - public void testKeyOnlyPut() throws Exception { - /* - * Testing this scenario - * (1) An empty table with the schema: - * - * "key" (HASH) - * - * (2) A POJO class: - * "key" (HASH), "attribute" (NON-KEY) - * - * (3) Save operation by some user: - * - item : {"key" : "some value"} - * - user-specified expected values : {"attribute" : {Exist : true}} - * - SaveBehavior : UPDATE (default) - * - * (4) Expected behavior - * ConditionalCheckFailedException, and the table should remain empty. - */ - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - HashAndAttribute obj = getUniqueObject(new HashAndAttribute()); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (HashAndAttribute obj : objs) { - try { - DynamoDbSaveExpression saveExpression = new DynamoDbSaveExpression(); - Map expected = new HashMap(); - ExpectedAttributeValue expectedVersion = ExpectedAttributeValue.builder() - .value(AttributeValue.builder() - .s("SomeNonExistantValue").build()) - .exists(true).build(); - expected.put("normalStringAttribute", expectedVersion); - saveExpression.setExpected(expected); - - util.save(obj, saveExpression); - fail("This should fail, expected clause should block an insert."); - } catch (ConditionalCheckFailedException e) { - // Ignored or expected. - } - assertNull(util.load(HashAndAttribute.class, obj.getKey())); - - //this should succeed without the expected clause - obj.setNormalStringAttribute("to-be-deleted"); - util.save(obj); - obj.setNormalStringAttribute(null); - util.save(obj); - Object loaded = util.load(HashAndAttribute.class, obj.getKey()); - assertEquals("Expected " + obj.toString() + ", but was " + loaded.toString(), obj, loaded); - } - } - - private T getUniqueObject(T obj) { - obj.setKey("" + startKey++); - return obj; - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class HashAndAttribute { - - protected String key; - protected String normalStringAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute - public String normalStringAttribute() { - return normalStringAttribute; - } - - public void setNormalStringAttribute(String normalStringAttribute) { - this.normalStringAttribute = normalStringAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - HashAndAttribute other = (HashAndAttribute) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (normalStringAttribute == null) { - if (other.normalStringAttribute != null) { - return false; - } - } else if (!normalStringAttribute.equals(other.normalStringAttribute)) { - return false; - } - return true; - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperLoadingStrategyConfigIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperLoadingStrategyConfigIntegrationTest.java deleted file mode 100644 index d466637c1dce..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperLoadingStrategyConfigIntegrationTest.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.ConsistentRead; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.PaginationLoadingStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbScanExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.PaginatedList; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.pojos.RangeKeyClass; - -/** - * Integration tests for PaginationLoadingStrategy configuration - */ -public class MapperLoadingStrategyConfigIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static long hashKey = System.currentTimeMillis(); - private static int PAGE_SIZE = 5; - private static int PARALLEL_SEGMENT = 3; - private static int OBJECTS_NUM = 50; - private static int RESULTS_NUM = OBJECTS_NUM - 2; // condition: rangeKey > 1.0 - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - createTestData(); - } - - private static void createTestData() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - List objs = new ArrayList(); - for (int i = 0; i < OBJECTS_NUM; i++) { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(hashKey); - obj.setRangeKey(i); - objs.add(obj); - } - - mapper.batchSave(objs); - } - - private static PaginatedList getTestPaginatedQueryList(PaginationLoadingStrategy paginationLoadingStrategy) { - DynamoDbMapperConfig mapperConfig = new DynamoDbMapperConfig(ConsistentRead.CONSISTENT); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo, mapperConfig); - - // Construct the query expression for the tested hash-key value and any range-key value greater that 1.0 - RangeKeyClass keyObject = new RangeKeyClass(); - keyObject.setKey(hashKey); - DynamoDbQueryExpression queryExpression = new DynamoDbQueryExpression() - .withHashKeyValues(keyObject); - queryExpression.withRangeKeyCondition("rangeKey", - Condition.builder().comparisonOperator(ComparisonOperator.GT.toString()) - .attributeValueList( - AttributeValue.builder().n("1.0").build()).build()).withLimit(PAGE_SIZE); - - return mapper.query(RangeKeyClass.class, queryExpression, new DynamoDbMapperConfig(paginationLoadingStrategy)); - } - - private static PaginatedList getTestPaginatedScanList(PaginationLoadingStrategy paginationLoadingStrategy) { - DynamoDbMapperConfig mapperConfig = new DynamoDbMapperConfig(ConsistentRead.CONSISTENT); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo, mapperConfig); - - // Construct the scan expression with the exact same conditions - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression(); - scanExpression.addFilterCondition("key", - Condition.builder().comparisonOperator(ComparisonOperator.EQ).attributeValueList( - AttributeValue.builder().n(Long.toString(hashKey)).build()).build()); - scanExpression.addFilterCondition("rangeKey", - Condition.builder().comparisonOperator(ComparisonOperator.GT).attributeValueList( - AttributeValue.builder().n("1.0").build()).build()); - scanExpression.setLimit(PAGE_SIZE); - - return mapper.scan(RangeKeyClass.class, scanExpression, new DynamoDbMapperConfig(paginationLoadingStrategy)); - } - - private static PaginatedList getTestPaginatedParallelScanList( - PaginationLoadingStrategy paginationLoadingStrategy) { - DynamoDbMapperConfig mapperConfig = new DynamoDbMapperConfig(ConsistentRead.CONSISTENT); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo, mapperConfig); - - // Construct the scan expression with the exact same conditions - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression(); - scanExpression.addFilterCondition("key", - Condition.builder().comparisonOperator(ComparisonOperator.EQ).attributeValueList( - AttributeValue.builder().n(Long.toString(hashKey)).build()).build()); - scanExpression.addFilterCondition("rangeKey", - Condition.builder().comparisonOperator(ComparisonOperator.GT).attributeValueList( - AttributeValue.builder().n("1.0").build()).build()); - scanExpression.setLimit(PAGE_SIZE); - - return mapper.parallelScan(RangeKeyClass.class, scanExpression, PARALLEL_SEGMENT, - new DynamoDbMapperConfig(paginationLoadingStrategy)); - } - - private static void testAllPaginatedListOperations(PaginatedList list) { - - // (1) isEmpty() - assertFalse(list.isEmpty()); - - // (2) get(int n) - assertNotNull(list.get(RESULTS_NUM / 2)); - - // (3) contains(Object org0) - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(hashKey); - obj.setRangeKey(0); - assertFalse(list.contains(obj)); - obj.setRangeKey(2); - assertTrue(list.contains(obj)); - - // (4) subList(int org0, int arg1) - List subList = list.subList(0, RESULTS_NUM); - assertEquals(RESULTS_NUM, subList.size()); - try { - list.subList(0, RESULTS_NUM + 1); - fail("IndexOutOfBoundsException is IndexOutOfBoundsException but not thrown"); - } catch (IndexOutOfBoundsException e) { - // Ignored or expected. - } - - // (5) indexOf(Object org0) - assertTrue(list.indexOf(obj) < RESULTS_NUM); - - // (6) loadAllResults() - list.loadAllResults(); - - // (7) size() - assertEquals(RESULTS_NUM, list.size()); - - } - - private static void testPaginatedListIterator(PaginatedList list) { - for (RangeKeyClass item : list) { - assertEquals(hashKey, item.getKey()); - assertTrue(item.getRangeKey() < OBJECTS_NUM); - } - - // make sure the list could be iterated again - for (RangeKeyClass item : list) { - assertEquals(hashKey, item.getKey()); - assertTrue(item.getRangeKey() < OBJECTS_NUM); - } - } - - private static void testIterationOnlyPaginatedListOperations(PaginatedList list) { - - // Unsupported operations - - // (1) isEmpty() - try { - list.isEmpty(); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - // (2) get(int n) - try { - list.get(RESULTS_NUM / 2); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - // (3) contains(Object org0) - try { - list.contains(new RangeKeyClass()); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - // (4) subList(int org0, int arg1) - try { - list.subList(0, RESULTS_NUM); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - // (5) indexOf(Object org0) - try { - list.indexOf(new RangeKeyClass()); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - // (6) loadAllResults() - try { - list.loadAllResults(); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - // (7) size() - try { - list.size(); - fail("UnsupportedOperationException expected but is not thrown"); - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - ; - - // Could be iterated once - for (RangeKeyClass item : list) { - assertEquals(hashKey, item.getKey()); - assertTrue(item.getRangeKey() < OBJECTS_NUM); - // At most one page of results in memeory - assertTrue(loadedResultsNumber(list) <= PAGE_SIZE); - } - - // not twice - try { - for (@SuppressWarnings("unused") RangeKeyClass item : list) { - fail("UnsupportedOperationException expected but is not thrown"); - } - } catch (UnsupportedOperationException e) { - // Ignored or expected. - } - - } - - /** - * Use reflection to get the size of the private allResults field - **/ - @SuppressWarnings("unchecked") - private static int loadedResultsNumber(PaginatedList list) { - Field privateAllResults = null; - try { - privateAllResults = list.getClass().getSuperclass().getDeclaredField("allResults"); - } catch (SecurityException e) { - fail(e.getMessage()); - } catch (NoSuchFieldException e) { - fail(e.getMessage()); - } - privateAllResults.setAccessible(true); - List allResults = null; - try { - allResults = (List) privateAllResults.get(list); - } catch (IllegalArgumentException e) { - fail(e.getMessage()); - } catch (IllegalAccessException e) { - fail(e.getMessage()); - } - return allResults.size(); - } - - @Test - public void testLazyLoading() { - // Get all the paginated lists using the tested loading strategy - PaginatedList queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); - PaginatedList scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); - PaginatedList parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); - - // check that only at most one page of results are loaded up to this point - assertTrue(loadedResultsNumber(queryList) <= PAGE_SIZE); - assertTrue(loadedResultsNumber(scanList) <= PAGE_SIZE); - assertTrue(loadedResultsNumber(parallelScanList) <= PAGE_SIZE * PARALLEL_SEGMENT); - - testAllPaginatedListOperations(queryList); - testAllPaginatedListOperations(scanList); - testAllPaginatedListOperations(parallelScanList); - - // Re-construct the paginated lists and test the iterator behavior - queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); - scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); - parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); - - testPaginatedListIterator(queryList); - testPaginatedListIterator(scanList); - testPaginatedListIterator(parallelScanList); - - } - - @Test - public void testEagerLoading() { - // Get all the paginated lists using the tested loading strategy - PaginatedList queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.EAGER_LOADING); - PaginatedList scanList = getTestPaginatedScanList(PaginationLoadingStrategy.EAGER_LOADING); - PaginatedList parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.EAGER_LOADING); - - // check that all results have been loaded - assertEquals(RESULTS_NUM, loadedResultsNumber(queryList)); - assertEquals(RESULTS_NUM, loadedResultsNumber(scanList)); - assertEquals(RESULTS_NUM, loadedResultsNumber(parallelScanList)); - - testAllPaginatedListOperations(queryList); - testAllPaginatedListOperations(scanList); - testAllPaginatedListOperations(parallelScanList); - - // Re-construct the paginated lists and test the iterator behavior - queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.LAZY_LOADING); - scanList = getTestPaginatedScanList(PaginationLoadingStrategy.LAZY_LOADING); - parallelScanList = getTestPaginatedParallelScanList(PaginationLoadingStrategy.LAZY_LOADING); - - testPaginatedListIterator(queryList); - testPaginatedListIterator(scanList); - testPaginatedListIterator(parallelScanList); - } - - @Test - public void testIterationOnly() { - // Get all the paginated lists using the tested loading strategy - PaginatedList queryList = getTestPaginatedQueryList(PaginationLoadingStrategy.ITERATION_ONLY); - PaginatedList scanList = getTestPaginatedScanList(PaginationLoadingStrategy.ITERATION_ONLY); - PaginatedList parallelScanList = getTestPaginatedParallelScanList( - PaginationLoadingStrategy.ITERATION_ONLY); - - // check that only at most one page of results are loaded up to this point - assertTrue(loadedResultsNumber(queryList) <= PAGE_SIZE); - assertTrue(loadedResultsNumber(scanList) <= PAGE_SIZE); - assertTrue(loadedResultsNumber(parallelScanList) <= PAGE_SIZE * PARALLEL_SEGMENT); - - testIterationOnlyPaginatedListOperations(queryList); - testIterationOnlyPaginatedListOperations(scanList); - testIterationOnlyPaginatedListOperations(parallelScanList); - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperSaveConfigIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperSaveConfigIntegrationTest.java deleted file mode 100644 index cf4feaa0f0fe..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperSaveConfigIntegrationTest.java +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.awscore.exception.AwsServiceException; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; - -/** - * Tests the behavior of save method of DynamoDBMapper under different - * SaveBehavior configurations. - */ -public class MapperSaveConfigIntegrationTest extends MapperSaveConfigTestBase { - - /********************************************* - ** UPDATE (default) ** - *********************************************/ - - private static TestItem putRandomUniqueItem(String nonKeyAttributeValue, Set stringSetAttributeValue) { - String hashKeyValue = UUID.randomUUID().toString(); - Long rangeKeyValue = System.currentTimeMillis(); - Map item = new HashMap(); - item.put(hashKeyName, AttributeValue.builder().s(hashKeyValue).build()); - item.put(rangeKeyName, AttributeValue.builder().n(rangeKeyValue.toString()).build()); - if (null != nonKeyAttributeValue) { - item.put(nonKeyAttributeName, AttributeValue.builder().s(nonKeyAttributeValue).build()); - } - if (null != stringSetAttributeValue) { - item.put(stringSetAttributeName, AttributeValue.builder().ss(stringSetAttributeValue).build()); - } - dynamo.putItem(PutItemRequest.builder().tableName(tableName).item(item).build()); - - /* Returns the item as a modeled object. */ - TestItem testItem = new TestItem(); - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute(nonKeyAttributeValue); - testItem.setStringSetAttribute(stringSetAttributeValue); - return testItem; - } - - private static Set generateRandomStringSet(int size) { - Set result = new HashSet(); - for (int i = 0; i < size; i++) { - result.add(UUID.randomUUID().toString()); - } - return result; - } - - private static boolean assertSetEquals(Set expected, Set actual) { - if (expected == null || actual == null) { - return (expected == null && actual == null); - } - if (expected.size() != actual.size()) { - return false; - } - for (Object item : expected) { - if (!actual.contains(item)) { - return false; - } - } - return true; - } - - /** - * Tests that a key-only object could be saved with - * UPDATE configuration, even when the key has already existed in the table. - */ - @Test - public void testDefaultWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (with non-key attribute)*/ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Put an key-only object with the same key. */ - testItem.setNonKeyAttribute(null); - - dynamoMapper.save(testItem, defaultConfig); - - /* The non-key attribute should be nulled out. */ - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /********************************************* - ** UPDATE_SKIP_NULL_ATTRIBUTES ** - *********************************************/ - - /** - * Tests an edge case that we have fixed according a forum bug report. If - * the object is only specified with key attributes, and such key is not - * present in the table, we should add this object by a key-only put - * request even if it is using UPDATE configuration. - */ - @Test - public void testDefaultWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, defaultConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Update an existing item in the table. - */ - @Test - public void testDefaultWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (without non-key attribute)*/ - TestItem testItem = putRandomUniqueItem(null, null); - String hashKeyValue = testItem.getHashKey(); - Long rangeKeyValue = testItem.getRangeKey(); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(hashKeyValue, returnedObject.getHashKey()); - assertEquals(rangeKeyValue, returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - - /* Put an updated object with the same key and an additional non-key attribute. */ - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute("update"); - - dynamoMapper.save(testItem, defaultConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /** - * Use UPDATE to put a new item in the table. - */ - @Test - public void testDefaultWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - - dynamoMapper.save(testItem, defaultConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /** - * When using UPDATE_SKIP_NULL_ATTRIBUTES, key-only update on existing item - * should not affect the item at all, since all the null-valued non-key - * attributes are ignored. - */ - @Test - public void testUpdateSkipNullWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (with non-key attribute)*/ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Put an key-only object with the same key. */ - testItem.setNonKeyAttribute(null); - - dynamoMapper.save(testItem, updateSkipNullConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - /* The non-key attribute should not be removed. */ - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals("foo", returnedObject.getNonKeyAttribute()); - } - - /********************************************* - ** APPEND_SET ** - *********************************************/ - - /** - * The behavior should be the same as UPDATE. - */ - @Test - public void testUpdateSkipNullWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, updateSkipNullConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Use UPDATE_SKIP_NULL_ATTRIBUTES to update an existing item in the table. - */ - @Test - public void testUpdateSkipNullWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (without non-key attribute)*/ - TestItem testItem = putRandomUniqueItem(null, null); - String hashKeyValue = testItem.getHashKey(); - Long rangeKeyValue = testItem.getRangeKey(); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(hashKeyValue, returnedObject.getHashKey()); - assertEquals(rangeKeyValue, returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - - /* Put an updated object with the same key and an additional non-key attribute. */ - String nonKeyAttributeValue = "update"; - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute(nonKeyAttributeValue); - - dynamoMapper.save(testItem, updateSkipNullConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - - /* At last, save the object again, but with non-key attribute set as null. - * This should not change the existing item. - */ - testItem.setNonKeyAttribute(null); - dynamoMapper.save(testItem, updateSkipNullConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(nonKeyAttributeValue, returnedObject.getNonKeyAttribute()); - } - - /** - * Use UPDATE_SKIP_NULL_ATTRIBUTES to put a new item in the table. - */ - @Test - public void testUpdateSkipNullWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - - dynamoMapper.save(testItem, updateSkipNullConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /** - * The behavior should be the same as UPDATE_SKIP_NULL_ATTRIBUTES. - */ - @Test - public void testAppendSetWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (with non-key attributes)*/ - Set randomSet = generateRandomStringSet(3); - TestItem testItem = putRandomUniqueItem("foo", randomSet); - - /* Put an key-only object with the same key. */ - testItem.setNonKeyAttribute(null); - testItem.setStringSetAttribute(null); - - dynamoMapper.save(testItem, appendSetConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - /* The non-key attribute should not be removed. */ - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals("foo", returnedObject.getNonKeyAttribute()); - assertTrue(assertSetEquals(randomSet, returnedObject.getStringSetAttribute())); - } - - /********************************************* - ** CLOBBER ** - *********************************************/ - - /** - * The behavior should be the same as UPDATE and UPDATE_SKIP_NULL_ATTRIBUTES. - */ - @Test - public void testAppendSetWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, appendSetConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - assertNull(returnedObject.getStringSetAttribute()); - } - - /** - * Use APPEND_SET to update an existing item in the table. - */ - @Test - public void testAppendSetWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - - /* First put a new item (without non-key attribute)*/ - TestItem testItem = putRandomUniqueItem(null, null); - String hashKeyValue = testItem.getHashKey(); - Long rangeKeyValue = testItem.getRangeKey(); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(hashKeyValue, returnedObject.getHashKey()); - assertEquals(rangeKeyValue, returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - assertNull(returnedObject.getStringSetAttribute()); - - /* Put an updated object with the same key and an additional non-key attribute. */ - String nonKeyAttributeValue = "update"; - Set stringSetAttributeValue = generateRandomStringSet(3); - testItem.setHashKey(hashKeyValue); - testItem.setRangeKey(rangeKeyValue); - testItem.setNonKeyAttribute(nonKeyAttributeValue); - testItem.setStringSetAttribute(stringSetAttributeValue); - - dynamoMapper.save(testItem, appendSetConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - assertTrue(assertSetEquals(testItem.getStringSetAttribute(), returnedObject.getStringSetAttribute())); - - /* Override nonKeyAttribute and append stringSetAttribute. */ - testItem.setNonKeyAttribute("blabla"); - Set appendSetAttribute = generateRandomStringSet(3); - testItem.setStringSetAttribute(appendSetAttribute); - dynamoMapper.save(testItem, appendSetConfig); - returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals("blabla", returnedObject.getNonKeyAttribute()); - // expected set after the append - stringSetAttributeValue.addAll(appendSetAttribute); - assertTrue(assertSetEquals(stringSetAttributeValue, returnedObject.getStringSetAttribute())); - - /* Append on an existing scalar attribute would result in an exception. */ - TestAppendToScalarItem testAppendToScalarItem = new TestAppendToScalarItem(); - testAppendToScalarItem.setHashKey(testItem.getHashKey()); - testAppendToScalarItem.setRangeKey(testItem.getRangeKey()); - // this fake set attribute actually points to a scalar attribute - testAppendToScalarItem.setFakeStringSetAttribute(generateRandomStringSet(1)); - try { - dynamoMapper.save(testAppendToScalarItem, appendSetConfig); - fail("Should have thrown a 'Type mismatch' service exception."); - } catch (AwsServiceException exception) { - assertEquals("ValidationException", exception.awsErrorDetails().errorCode()); - } - } - - /** - * Use APPEND_SET to put a new item in the table. - */ - @Test - public void testAppendSetWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - testItem.setStringSetAttribute(generateRandomStringSet(3)); - - dynamoMapper.save(testItem, appendSetConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - assertEquals(testItem.getStringSetAttribute(), returnedObject.getStringSetAttribute()); - - } - - /** - * Use CLOBBER to override the existing item by saving a key-only object. - */ - @Test - public void testClobberWithOnlyKeyAttributesSpecifiedRecordInTable() - throws Exception { - /* Put the item with non-key attribute. */ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Override the item by saving a key-only object. */ - testItem.setNonKeyAttribute(null); - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Use CLOBBER to put a new item with only key attributes. - */ - @Test - public void testClobberWithOnlyKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertNull(returnedObject.getNonKeyAttribute()); - } - - /** - * Use CLOBBER to override the existing item. - */ - @Test - public void testClobberWithKeyAndNonKeyAttributesSpecifiedRecordInTable() - throws Exception { - /* Put the item with non-key attribute. */ - TestItem testItem = putRandomUniqueItem("foo", null); - - /* Override the item. */ - testItem.setNonKeyAttribute("not foo"); - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } - - /** - * Use CLOBBER to put a new item. - */ - @Test - public void testClobberWithKeyAndNonKeyAttributesSpecifiedRecordNotInTable() - throws Exception { - TestItem testItem = new TestItem(); - testItem.setHashKey(UUID.randomUUID().toString()); - testItem.setRangeKey(System.currentTimeMillis()); - testItem.setNonKeyAttribute("new item"); - - dynamoMapper.save(testItem, clobberConfig); - - TestItem returnedObject = (TestItem) dynamoMapper.load(testItem); - - assertNotNull(returnedObject); - assertEquals(testItem.getHashKey(), returnedObject.getHashKey()); - assertEquals(testItem.getRangeKey(), returnedObject.getRangeKey()); - assertEquals(testItem.getNonKeyAttribute(), returnedObject.getNonKeyAttribute()); - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperSaveConfigTestBase.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperSaveConfigTestBase.java deleted file mode 100644 index 723dfd941c4a..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/MapperSaveConfigTestBase.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.Set; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import utils.test.util.DynamoDBIntegrationTestBase; - -public class MapperSaveConfigTestBase extends DynamoDBIntegrationTestBase { - - protected static final DynamoDbMapperConfig defaultConfig = new DynamoDbMapperConfig( - SaveBehavior.UPDATE); - protected static final DynamoDbMapperConfig updateSkipNullConfig = new DynamoDbMapperConfig( - SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES); - protected static final DynamoDbMapperConfig appendSetConfig = new DynamoDbMapperConfig( - SaveBehavior.APPEND_SET); - protected static final DynamoDbMapperConfig clobberConfig = new DynamoDbMapperConfig( - SaveBehavior.CLOBBER); - protected static final String tableName = "aws-java-sdk-dynamodb-mapper-save-config-test"; - protected static final String hashKeyName = "hashKey"; - protected static final String rangeKeyName = "rangeKey"; - protected static final String nonKeyAttributeName = "nonKeyAttribute"; - protected static final String stringSetAttributeName = "stringSetAttribute"; - /** - * Read capacity for the test table being created in Amazon DynamoDB. - */ - protected static final Long READ_CAPACITY = 10L; - /** - * Write capacity for the test table being created in Amazon DynamoDB. - */ - protected static final Long WRITE_CAPACITY = 5L; - /** - * Provisioned Throughput for the test table created in Amazon DynamoDB - */ - protected static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = ProvisionedThroughput.builder() - .readCapacityUnits(READ_CAPACITY).writeCapacityUnits( - WRITE_CAPACITY).build(); - protected static DynamoDbMapper dynamoMapper; - - @BeforeClass - public static void setUp() throws Exception { - setUpCredentials(); - dynamo = DynamoDbClient.builder().credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - dynamoMapper = new DynamoDbMapper(dynamo); - - createTestTable(DEFAULT_PROVISIONED_THROUGHPUT); - TableUtils.waitUntilActive(dynamo, tableName); - } - - @AfterClass - public static void tearDown() { - dynamo.deleteTable(DeleteTableRequest.builder().tableName(tableName).build()); - } - - /** - * Helper method to create a table in Amazon DynamoDB - */ - protected static void createTestTable( - ProvisionedThroughput provisionedThroughput) { - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(tableName) - .keySchema( - KeySchemaElement.builder().attributeName( - hashKeyName).keyType( - KeyType.HASH).build(), - KeySchemaElement.builder().attributeName( - rangeKeyName).keyType( - KeyType.RANGE).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName( - hashKeyName).attributeType( - ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName( - rangeKeyName).attributeType( - ScalarAttributeType.N).build()) - .provisionedThroughput(provisionedThroughput) - .build(); - - TableDescription createdTableDescription = dynamo.createTable( - createTableRequest).tableDescription(); - System.out.println("Created Table: " + createdTableDescription); - assertEquals(tableName, createdTableDescription.tableName()); - assertNotNull(createdTableDescription.tableStatus()); - assertEquals(hashKeyName, createdTableDescription - .keySchema().get(0).attributeName()); - assertEquals(KeyType.HASH, createdTableDescription - .keySchema().get(0).keyType()); - assertEquals(rangeKeyName, createdTableDescription - .keySchema().get(1).attributeName()); - assertEquals(KeyType.RANGE, createdTableDescription - .keySchema().get(1).keyType()); - } - - @DynamoDbTable(tableName = tableName) - public static class TestItem { - - private String hashKey; - private Long rangeKey; - private String nonKeyAttribute; - private Set stringSetAttribute; - - @DynamoDbHashKey(attributeName = hashKeyName) - public String getHashKey() { - return hashKey; - } - - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - @DynamoDbRangeKey(attributeName = rangeKeyName) - public Long getRangeKey() { - return rangeKey; - } - - public void setRangeKey(Long rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbAttribute(attributeName = nonKeyAttributeName) - public String getNonKeyAttribute() { - return nonKeyAttribute; - } - - public void setNonKeyAttribute(String nonKeyAttribute) { - this.nonKeyAttribute = nonKeyAttribute; - } - - @DynamoDbAttribute(attributeName = stringSetAttributeName) - public Set getStringSetAttribute() { - return stringSetAttribute; - } - - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } - - } - - @DynamoDbTable(tableName = tableName) - public static class TestAppendToScalarItem { - - private String hashKey; - private Long rangeKey; - private Set fakeStringSetAttribute; - - @DynamoDbHashKey(attributeName = hashKeyName) - public String getHashKey() { - return hashKey; - } - - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - @DynamoDbRangeKey(attributeName = rangeKeyName) - public Long getRangeKey() { - return rangeKey; - } - - public void setRangeKey(Long rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbAttribute(attributeName = nonKeyAttributeName) - public Set getFakeStringSetAttribute() { - return fakeStringSetAttribute; - } - - public void setFakeStringSetAttribute(Set stringSetAttribute) { - this.fakeStringSetAttribute = stringSetAttribute; - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NoSuchTableClass.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NoSuchTableClass.java deleted file mode 100644 index d3d91bfb4f80..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NoSuchTableClass.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -@DynamoDbTable(tableName = "tableNotExist") -public class NoSuchTableClass { - - private String key; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumberAttributeClass.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumberAttributeClass.java deleted file mode 100644 index 858c6692f634..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumberAttributeClass.java +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIgnore; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Simple domain class with numeric attributes - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class NumberAttributeClass { - - private String key; - private int intAttribute; - private Integer integerAttribute; - private double doubleAttribute; - private Double doubleObjectAttribute; - private float floatAttribute; - private Float floatObjectAttribute; - private BigDecimal bigDecimalAttribute; - private BigInteger bigIntegerAttribute; - private long longAttribute; - private Long longObjectAttribute; - private short shortAttribute; - private Short shortObjectAttribute; - private byte byteAttribute; - private Byte byteObjectAttribute; - private Date dateAttribute; - private Calendar calendarAttribute; - private Boolean booleanObjectAttribute; - private boolean booleanAttribute; - private String ignored = "notSent"; - - @DynamoDbAutoGeneratedKey - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public int getIntAttribute() { - return intAttribute; - } - - public void setIntAttribute(int intAttribute) { - this.intAttribute = intAttribute; - } - - public Integer getIntegerAttribute() { - return integerAttribute; - } - - public void setIntegerAttribute(Integer integerAttribute) { - this.integerAttribute = integerAttribute; - } - - public double getDoubleAttribute() { - return doubleAttribute; - } - - public void setDoubleAttribute(double doubleAttribute) { - this.doubleAttribute = doubleAttribute; - } - - public Double getDoubleObjectAttribute() { - return doubleObjectAttribute; - } - - public void setDoubleObjectAttribute(Double doubleObjectAttribute) { - this.doubleObjectAttribute = doubleObjectAttribute; - } - - @DynamoDbAttribute - public float getFloatAttribute() { - return floatAttribute; - } - - public void setFloatAttribute(float floatAttribute) { - this.floatAttribute = floatAttribute; - } - - public Float getFloatObjectAttribute() { - return floatObjectAttribute; - } - - public void setFloatObjectAttribute(Float floatObjectAttribute) { - this.floatObjectAttribute = floatObjectAttribute; - } - - public BigDecimal getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - public BigInteger getBigIntegerAttribute() { - return bigIntegerAttribute; - } - - public void setBigIntegerAttribute(BigInteger bigIntegerAttribute) { - this.bigIntegerAttribute = bigIntegerAttribute; - } - - public long getLongAttribute() { - return longAttribute; - } - - public void setLongAttribute(long longAttribute) { - this.longAttribute = longAttribute; - } - - public Long getLongObjectAttribute() { - return longObjectAttribute; - } - - public void setLongObjectAttribute(Long longObjectAttribute) { - this.longObjectAttribute = longObjectAttribute; - } - - public byte getByteAttribute() { - return byteAttribute; - } - - public void setByteAttribute(byte byteAttribute) { - this.byteAttribute = byteAttribute; - } - - public Byte getByteObjectAttribute() { - return byteObjectAttribute; - } - - public void setByteObjectAttribute(Byte byteObjectAttribute) { - this.byteObjectAttribute = byteObjectAttribute; - } - - public Date getDateAttribute() { - return dateAttribute; - } - - public void setDateAttribute(Date dateAttribute) { - this.dateAttribute = dateAttribute; - } - - public Calendar getCalendarAttribute() { - return calendarAttribute; - } - - public void setCalendarAttribute(Calendar calendarAttribute) { - this.calendarAttribute = calendarAttribute; - } - - public Boolean getBooleanObjectAttribute() { - return booleanObjectAttribute; - } - - public void setBooleanObjectAttribute(Boolean booleanObjectAttribute) { - this.booleanObjectAttribute = booleanObjectAttribute; - } - - public boolean isBooleanAttribute() { - return booleanAttribute; - } - - public void setBooleanAttribute(boolean booleanAttribute) { - this.booleanAttribute = booleanAttribute; - } - - @DynamoDbIgnore - public String getIgnored() { - return ignored; - } - - public void setIgnored(String ignored) { - this.ignored = ignored; - } - - public short getShortAttribute() { - return shortAttribute; - } - - public void setShortAttribute(short shortAttribute) { - this.shortAttribute = shortAttribute; - } - - public Short getShortObjectAttribute() { - return shortObjectAttribute; - } - - public void setShortObjectAttribute(Short shortObjectAttribute) { - this.shortObjectAttribute = shortObjectAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); - result = prime * result + (booleanAttribute ? 1231 : 1237); - result = prime * result + ((booleanObjectAttribute == null) ? 0 : booleanObjectAttribute.hashCode()); - result = prime * result + byteAttribute; - result = prime * result + ((byteObjectAttribute == null) ? 0 : byteObjectAttribute.hashCode()); - result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); - result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); - long temp; - temp = Double.doubleToLongBits(doubleAttribute); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((doubleObjectAttribute == null) ? 0 : doubleObjectAttribute.hashCode()); - result = prime * result + Float.floatToIntBits(floatAttribute); - result = prime * result + ((floatObjectAttribute == null) ? 0 : floatObjectAttribute.hashCode()); - result = prime * result + ((ignored == null) ? 0 : ignored.hashCode()); - result = prime * result + intAttribute; - result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + (int) (longAttribute ^ (longAttribute >>> 32)); - result = prime * result + ((longObjectAttribute == null) ? 0 : longObjectAttribute.hashCode()); - result = prime * result + shortAttribute; - result = prime * result + ((shortObjectAttribute == null) ? 0 : shortObjectAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - NumberAttributeClass other = (NumberAttributeClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) { - return false; - } - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) { - return false; - } - if (bigIntegerAttribute == null) { - if (other.bigIntegerAttribute != null) { - return false; - } - } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) { - return false; - } - if (booleanAttribute != other.booleanAttribute) { - return false; - } - if (booleanObjectAttribute == null) { - if (other.booleanObjectAttribute != null) { - return false; - } - } else if (!booleanObjectAttribute.equals(other.booleanObjectAttribute)) { - return false; - } - if (byteAttribute != other.byteAttribute) { - return false; - } - if (byteObjectAttribute == null) { - if (other.byteObjectAttribute != null) { - return false; - } - } else if (!byteObjectAttribute.equals(other.byteObjectAttribute)) { - return false; - } - if (calendarAttribute == null) { - if (other.calendarAttribute != null) { - return false; - } - } else if (!calendarAttribute.equals(other.calendarAttribute)) { - return false; - } - if (dateAttribute == null) { - if (other.dateAttribute != null) { - return false; - } - } else if (!dateAttribute.equals(other.dateAttribute)) { - return false; - } - if (Double.doubleToLongBits(doubleAttribute) != Double.doubleToLongBits(other.doubleAttribute)) { - return false; - } - if (doubleObjectAttribute == null) { - if (other.doubleObjectAttribute != null) { - return false; - } - } else if (!doubleObjectAttribute.equals(other.doubleObjectAttribute)) { - return false; - } - if (Float.floatToIntBits(floatAttribute) != Float.floatToIntBits(other.floatAttribute)) { - return false; - } - if (floatObjectAttribute == null) { - if (other.floatObjectAttribute != null) { - return false; - } - } else if (!floatObjectAttribute.equals(other.floatObjectAttribute)) { - return false; - } - if (ignored == null) { - if (other.ignored != null) { - return false; - } - } else if (!ignored.equals(other.ignored)) { - return false; - } - if (intAttribute != other.intAttribute) { - return false; - } - if (integerAttribute == null) { - if (other.integerAttribute != null) { - return false; - } - } else if (!integerAttribute.equals(other.integerAttribute)) { - return false; - } - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (longAttribute != other.longAttribute) { - return false; - } - if (longObjectAttribute == null) { - if (other.longObjectAttribute != null) { - return false; - } - } else if (!longObjectAttribute.equals(other.longObjectAttribute)) { - return false; - } - if (shortAttribute != other.shortAttribute) { - return false; - } - if (shortObjectAttribute == null) { - if (other.shortObjectAttribute != null) { - return false; - } - } else if (!shortObjectAttribute.equals(other.shortObjectAttribute)) { - return false; - } - return true; - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumberSetAttributeClass.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumberSetAttributeClass.java deleted file mode 100644 index a1fbfb4ac27f..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumberSetAttributeClass.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Simple domain class with numeric attributes - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class NumberSetAttributeClass { - - private String key; - private Set integerAttribute; - private Set doubleObjectAttribute; - private Set floatObjectAttribute; - private Set bigDecimalAttribute; - private Set bigIntegerAttribute; - private Set longObjectAttribute; - private Set byteObjectAttribute; - private Set dateAttribute; - private Set calendarAttribute; - private Set booleanAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute - public Set getIntegerAttribute() { - return integerAttribute; - } - - public void setIntegerAttribute(Set integerAttribute) { - this.integerAttribute = integerAttribute; - } - - @DynamoDbAttribute - public Set getDoubleObjectAttribute() { - return doubleObjectAttribute; - } - - public void setDoubleObjectAttribute(Set doubleObjectAttribute) { - this.doubleObjectAttribute = doubleObjectAttribute; - } - - @DynamoDbAttribute - public Set getFloatObjectAttribute() { - return floatObjectAttribute; - } - - public void setFloatObjectAttribute(Set floatObjectAttribute) { - this.floatObjectAttribute = floatObjectAttribute; - } - - @DynamoDbAttribute - public Set getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(Set bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - @DynamoDbAttribute - public Set getBigIntegerAttribute() { - return bigIntegerAttribute; - } - - public void setBigIntegerAttribute(Set bigIntegerAttribute) { - this.bigIntegerAttribute = bigIntegerAttribute; - } - - @DynamoDbAttribute - public Set getLongObjectAttribute() { - return longObjectAttribute; - } - - public void setLongObjectAttribute(Set longObjectAttribute) { - this.longObjectAttribute = longObjectAttribute; - } - - @DynamoDbAttribute - public Set getByteObjectAttribute() { - return byteObjectAttribute; - } - - public void setByteObjectAttribute(Set byteObjectAttribute) { - this.byteObjectAttribute = byteObjectAttribute; - } - - @DynamoDbAttribute - public Set getDateAttribute() { - return dateAttribute; - } - - public void setDateAttribute(Set dateAttribute) { - this.dateAttribute = dateAttribute; - } - - @DynamoDbAttribute - public Set getCalendarAttribute() { - return calendarAttribute; - } - - public void setCalendarAttribute(Set calendarAttribute) { - this.calendarAttribute = calendarAttribute; - } - - @DynamoDbAttribute - public Set getBooleanAttribute() { - return booleanAttribute; - } - - public void setBooleanAttribute(Set booleanAttribute) { - this.booleanAttribute = booleanAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); - result = prime * result + ((booleanAttribute == null) ? 0 : booleanAttribute.hashCode()); - result = prime * result + ((byteObjectAttribute == null) ? 0 : byteObjectAttribute.hashCode()); - result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); - result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); - result = prime * result + ((doubleObjectAttribute == null) ? 0 : doubleObjectAttribute.hashCode()); - result = prime * result + ((floatObjectAttribute == null) ? 0 : floatObjectAttribute.hashCode()); - result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((longObjectAttribute == null) ? 0 : longObjectAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - NumberSetAttributeClass other = (NumberSetAttributeClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) { - return false; - } - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) { - return false; - } - if (bigIntegerAttribute == null) { - if (other.bigIntegerAttribute != null) { - return false; - } - } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) { - return false; - } - if (booleanAttribute == null) { - if (other.booleanAttribute != null) { - return false; - } - } else if (!booleanAttribute.equals(other.booleanAttribute)) { - return false; - } - if (byteObjectAttribute == null) { - if (other.byteObjectAttribute != null) { - return false; - } - } else if (!byteObjectAttribute.equals(other.byteObjectAttribute)) { - return false; - } - if (calendarAttribute == null) { - if (other.calendarAttribute != null) { - return false; - } - } else if (!calendarAttribute.equals(other.calendarAttribute)) { - return false; - } - if (dateAttribute == null) { - if (other.dateAttribute != null) { - return false; - } - } else if (!dateAttribute.equals(other.dateAttribute)) { - return false; - } - if (doubleObjectAttribute == null) { - if (other.doubleObjectAttribute != null) { - return false; - } - } else if (!doubleObjectAttribute.equals(other.doubleObjectAttribute)) { - return false; - } - if (floatObjectAttribute == null) { - if (other.floatObjectAttribute != null) { - return false; - } - } else if (!floatObjectAttribute.equals(other.floatObjectAttribute)) { - return false; - } - if (integerAttribute == null) { - if (other.integerAttribute != null) { - return false; - } - } else if (!integerAttribute.equals(other.integerAttribute)) { - return false; - } - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (longObjectAttribute == null) { - if (other.longObjectAttribute != null) { - return false; - } - } else if (!longObjectAttribute.equals(other.longObjectAttribute)) { - return false; - } - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "NumberSetAttributeClass [key=" + key; - // + ", integerAttribute=" + integerAttribute - // + ", doubleObjectAttribute=" + doubleObjectAttribute + ", floatObjectAttribute=" + floatObjectAttribute - // + ", bigDecimalAttribute=" + bigDecimalAttribute + ", bigIntegerAttribute=" + bigIntegerAttribute - // + ", longObjectAttribute=" + longObjectAttribute + ", byteObjectAttribute=" + byteObjectAttribute - // + ", dateAttribute=" + dateAttribute + ", calendarAttribute=" + calendarAttribute - // + ", booleanAttribute=" + booleanAttribute + "]"; - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumericSetAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumericSetAttributesIntegrationTest.java deleted file mode 100644 index 8b7138d72058..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/NumericSetAttributesIntegrationTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; - -/** - * Tests string set attributes - */ -public class NumericSetAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String INTEGER_ATTRIBUTE = "integerAttribute"; - private static final String FLOAT_OBJECT_ATTRIBUTE = "floatObjectAttribute"; - private static final String DOUBLE_OBJECT_ATTRIBUTE = "doubleObjectAttribute"; - private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; - private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; - private static final String LONG_OBJECT_ATTRIBUTE = "longObjectAttribute"; - private static final String BYTE_OBJECT_ATTRIBUTE = "byteObjectAttribute"; - private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; - private static final List> attrs = new LinkedList>(); - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = 1; - - // Test data - static { - for (int i = 0; i < 5; i++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().s("" + start++).build()); - attr.put(INTEGER_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(FLOAT_OBJECT_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(DOUBLE_OBJECT_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(BIG_INTEGER_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(BIG_DECIMAL_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(LONG_OBJECT_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(BYTE_OBJECT_ATTRIBUTE, AttributeValue.builder().ns("" + byteStart++, "" + byteStart++, "" + byteStart++).build()); - attr.put(BOOLEAN_ATTRIBUTE, AttributeValue.builder().ns("0", "1").build()); - attrs.add(attr); - } - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperIntegrationTestBase.setUp(); - - // Insert the data - for (Map attr : attrs) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(attr).build()); - } - } - - @Test - public void testLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - for (Map attr : attrs) { - NumberSetAttributeClass x = util.load(NumberSetAttributeClass.class, attr.get(KEY_NAME).s()); - assertEquals(x.getKey(), attr.get(KEY_NAME).s()); - - // Convert all numbers to the most inclusive type for easy comparison - assertNumericSetsEquals(x.getBigDecimalAttribute(), attr.get(BIG_DECIMAL_ATTRIBUTE).ns()); - assertNumericSetsEquals(x.getBigIntegerAttribute(), attr.get(BIG_INTEGER_ATTRIBUTE).ns()); - assertNumericSetsEquals(x.getFloatObjectAttribute(), attr.get(FLOAT_OBJECT_ATTRIBUTE).ns()); - assertNumericSetsEquals(x.getDoubleObjectAttribute(), attr.get(DOUBLE_OBJECT_ATTRIBUTE).ns()); - assertNumericSetsEquals(x.getIntegerAttribute(), attr.get(INTEGER_ATTRIBUTE).ns()); - assertNumericSetsEquals(x.getLongObjectAttribute(), attr.get(LONG_OBJECT_ATTRIBUTE).ns()); - assertNumericSetsEquals(x.getByteObjectAttribute(), attr.get(BYTE_OBJECT_ATTRIBUTE).ns()); - assertSetsEqual(toSet("0", "1"), attr.get(BOOLEAN_ATTRIBUTE).ns()); - } - } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - NumberSetAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (NumberSetAttributeClass obj : objs) { - util.save(obj); - } - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass loaded = util.load(NumberSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - NumberSetAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (NumberSetAttributeClass obj : objs) { - util.save(obj); - } - - for (NumberSetAttributeClass obj : objs) { - NumberSetAttributeClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - assertEquals(replacement, util.load(NumberSetAttributeClass.class, obj.getKey())); - } - } - - private NumberSetAttributeClass getUniqueObject() { - NumberSetAttributeClass obj = new NumberSetAttributeClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(toSet(new BigDecimal(startKey++), new BigDecimal(startKey++), new BigDecimal(startKey++))); - obj.setBigIntegerAttribute( - toSet(new BigInteger("" + startKey++), new BigInteger("" + startKey++), new BigInteger("" + startKey++))); - obj.setByteObjectAttribute(toSet(new Byte("" + byteStart++), new Byte("" + byteStart++), new Byte("" + byteStart++))); - obj.setDoubleObjectAttribute(toSet(new Double("" + start++), new Double("" + start++), new Double("" + start++))); - obj.setFloatObjectAttribute(toSet(new Float("" + start++), new Float("" + start++), new Float("" + start++))); - obj.setIntegerAttribute(toSet(new Integer("" + start++), new Integer("" + start++), new Integer("" + start++))); - obj.setLongObjectAttribute(toSet(new Long("" + start++), new Long("" + start++), new Long("" + start++))); - obj.setBooleanAttribute(toSet(true, false)); - obj.setDateAttribute(toSet(new Date(startKey++), new Date(startKey++), new Date(startKey++))); - Set cals = new HashSet(); - for (Date d : obj.getDateAttribute()) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(d); - cals.add(cal); - } - obj.setCalendarAttribute(toSet(cals)); - return obj; - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/QueryIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/QueryIntegrationTest.java deleted file mode 100644 index fc5838014dca..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/QueryIntegrationTest.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Random; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.pojos.RangeKeyClass; - -/** - * Integration tests for the query operation on DynamoDBMapper. - */ -public class QueryIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final long HASH_KEY = System.currentTimeMillis(); - private static final int TEST_ITEM_NUMBER = 500; - private static RangeKeyClass hashKeyObject; - private static DynamoDbMapper mapper; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - - DynamoDbMapperConfig mapperConfig = new DynamoDbMapperConfig(DynamoDbMapperConfig.ConsistentRead.CONSISTENT); - mapper = new DynamoDbMapper(dynamo, mapperConfig); - - putTestData(mapper, TEST_ITEM_NUMBER); - - hashKeyObject = new RangeKeyClass(); - hashKeyObject.setKey(HASH_KEY); - } - - /** - * Use BatchSave to put some test data into the tested table. Each item is - * hash-keyed by the same value, and range-keyed by numbers starting from 0. - */ - private static void putTestData(DynamoDbMapper mapper, int itemNumber) { - List objs = new ArrayList(); - for (int i = 0; i < itemNumber; i++) { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(HASH_KEY); - obj.setRangeKey(i); - obj.setBigDecimalAttribute(new BigDecimal(i)); - objs.add(obj); - } - mapper.batchSave(objs); - } - - @Test - public void testQueryWithPrimaryRangeKey() throws Exception { - DynamoDbQueryExpression queryExpression = - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyObject) - .withRangeKeyCondition( - "rangeKey", - Condition.builder() - .comparisonOperator(ComparisonOperator.GT) - .attributeValueList(AttributeValue.builder().n("1.0").build()).build()) - .withLimit(11); - List list = mapper.query(RangeKeyClass.class, queryExpression); - - int count = 0; - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - count++; - RangeKeyClass next = iterator.next(); - assertTrue(next.getRangeKey() > 1.00); - } - - int numMatchingObjects = TEST_ITEM_NUMBER - 2; - assertEquals(count, numMatchingObjects); - assertEquals(numMatchingObjects, list.size()); - - assertNotNull(list.get(list.size() / 2)); - assertTrue(list.contains(list.get(list.size() / 2))); - assertEquals(numMatchingObjects, list.toArray().length); - - Thread.sleep(250); - int totalCount = mapper.count(RangeKeyClass.class, queryExpression); - assertEquals(numMatchingObjects, totalCount); - - /** - * Tests query with only hash key - */ - queryExpression = new DynamoDbQueryExpression().withHashKeyValues(hashKeyObject); - list = mapper.query(RangeKeyClass.class, queryExpression); - assertEquals(TEST_ITEM_NUMBER, list.size()); - } - - /** - * Tests making queries using query filter on non-key attributes. - */ - @Test - public void testQueryFilter() { - // A random filter condition to be applied to the query. - Random random = new Random(); - int randomFilterValue = random.nextInt(TEST_ITEM_NUMBER); - Condition filterCondition = Condition.builder() - .comparisonOperator(ComparisonOperator.LT) - .attributeValueList( - AttributeValue.builder().n(Integer.toString(randomFilterValue)).build()).build(); - - /* - * (1) Apply the filter on the range key, in form of key condition - */ - DynamoDbQueryExpression queryWithRangeKeyCondition = - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyObject) - .withRangeKeyCondition("rangeKey", filterCondition); - List rangeKeyConditionResult = mapper.query(RangeKeyClass.class, queryWithRangeKeyCondition); - - /* - * (2) Apply the filter on the bigDecimalAttribute, in form of query filter - */ - DynamoDbQueryExpression queryWithQueryFilterCondition = - new DynamoDbQueryExpression() - .withHashKeyValues(hashKeyObject) - .withQueryFilter(Collections.singletonMap("bigDecimalAttribute", filterCondition)); - List queryFilterResult = mapper.query(RangeKeyClass.class, queryWithQueryFilterCondition); - - assertEquals(rangeKeyConditionResult.size(), queryFilterResult.size()); - for (int i = 0; i < rangeKeyConditionResult.size(); i++) { - assertEquals(rangeKeyConditionResult.get(i), queryFilterResult.get(i)); - } - } - - /** - * Tests that exception should be raised when user provides an index name - * when making query with the primary range key. - */ - @Test - public void testUnnecessaryIndexNameException() { - try { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - long hashKey = System.currentTimeMillis(); - RangeKeyClass keyObject = new RangeKeyClass(); - keyObject.setKey(hashKey); - DynamoDbQueryExpression queryExpression = new DynamoDbQueryExpression() - .withHashKeyValues(keyObject); - queryExpression.withRangeKeyCondition("rangeKey", - Condition.builder().comparisonOperator(ComparisonOperator.GT.toString()) - .attributeValueList( - AttributeValue.builder().n("1.0").build()).build()).withLimit(11) - .withIndexName("some_index"); - mapper.query(RangeKeyClass.class, queryExpression); - fail("User should not provide index name when making query with the primary range key"); - } catch (IllegalArgumentException expected) { - System.out.println(expected.getMessage()); - } catch (Exception e) { - fail("Should trigger SdkClientException."); - } - - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/RangeKeyAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/RangeKeyAttributesIntegrationTest.java deleted file mode 100644 index 32f1f6f642f0..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/RangeKeyAttributesIntegrationTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.pojos.RangeKeyClass; - -/** - * Tests range and hash key combination - */ -public class RangeKeyAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String RANGE_KEY = "rangeKey"; - private static final String INTEGER_ATTRIBUTE = "integerSetAttribute"; - private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; - private static final String STRING_SET_ATTRIBUTE = "stringSetAttribute"; - private static final String STRING_ATTRIBUTE = "stringAttribute"; - private static final String VERSION_ATTRIBUTE = "version"; - private static final List> attrs = new LinkedList>(); - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - - // Test data - static { - for (int i = 0; i < 5; i++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().n("" + startKey++).build()); - attr.put(RANGE_KEY, AttributeValue.builder().n("" + start++).build()); - attr.put(INTEGER_ATTRIBUTE, AttributeValue.builder().ns("" + start++, "" + start++, "" + start++).build()); - attr.put(BIG_DECIMAL_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(STRING_ATTRIBUTE, AttributeValue.builder().s("" + start++).build()); - attr.put(STRING_SET_ATTRIBUTE, AttributeValue.builder().ss("" + start++, "" + start++, "" + start++).build()); - attr.put(VERSION_ATTRIBUTE, AttributeValue.builder().n("1").build()); - - attrs.add(attr); - } - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - setUpTableWithRangeAttribute(); - - // Insert the data - for (Map attr : attrs) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_WITH_RANGE_ATTRIBUTE).item(attr).build()); - } - } - - @Test - public void testLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - for (Map attr : attrs) { - RangeKeyClass x = util.load(newRangeKey(Long.parseLong(attr.get(KEY_NAME).n()), - Double.parseDouble(attr.get(RANGE_KEY).n()))); - - // Convert all numbers to the most inclusive type for easy - // comparison - assertEquals(new BigDecimal(x.getKey()), new BigDecimal(attr.get(KEY_NAME).n())); - assertEquals(new BigDecimal(x.getRangeKey()), new BigDecimal(attr.get(RANGE_KEY).n())); - assertEquals(new BigDecimal(x.getVersion()), new BigDecimal(attr.get(VERSION_ATTRIBUTE).n())); - assertEquals(x.getBigDecimalAttribute(), new BigDecimal(attr.get(BIG_DECIMAL_ATTRIBUTE).n())); - assertNumericSetsEquals(x.getIntegerAttribute(), attr.get(INTEGER_ATTRIBUTE).ns()); - assertEquals(x.getStringAttribute(), attr.get(STRING_ATTRIBUTE).s()); - assertSetsEqual(x.getStringSetAttribute(), toSet(attr.get(STRING_SET_ATTRIBUTE).ss())); - } - } - - private RangeKeyClass newRangeKey(long hashKey, double rangeKey) { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(hashKey); - obj.setRangeKey(rangeKey); - return obj; - } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - RangeKeyClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (RangeKeyClass obj : objs) { - util.save(obj); - } - - for (RangeKeyClass obj : objs) { - RangeKeyClass loaded = util.load(RangeKeyClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - RangeKeyClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (RangeKeyClass obj : objs) { - util.save(obj); - } - - for (RangeKeyClass obj : objs) { - RangeKeyClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - replacement.setRangeKey(obj.getRangeKey()); - replacement.setVersion(obj.getVersion()); - util.save(replacement); - - RangeKeyClass loadedObject = util.load(RangeKeyClass.class, obj.getKey(), obj.getRangeKey()); - assertEquals(replacement, loadedObject); - - // If we try to update the old version, we should get an error - replacement.setVersion(replacement.getVersion() - 1); - try { - util.save(replacement); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - } - } - - private RangeKeyClass getUniqueObject() { - RangeKeyClass obj = new RangeKeyClass(); - obj.setKey(startKey++); - obj.setIntegerAttribute(toSet(start++, start++, start++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setRangeKey(start++); - obj.setStringAttribute("" + startKey++); - obj.setStringSetAttribute(toSet("" + startKey++, "" + startKey++, "" + startKey++)); - return obj; - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ScalarAttributeIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ScalarAttributeIntegrationTest.java deleted file mode 100644 index 840daaf8a03b..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ScalarAttributeIntegrationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbScalarAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; - -/** - * Status tests for {@code ScalarAttribute}. - */ -public class ScalarAttributeIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test with a non-null enum val. - */ - @Test - public void testMarshalling() { - final KeyAndBinaryUuid object = new KeyAndBinaryUuid(); - object.setVal(UUID.randomUUID()); - assertBeforeAndAfterChange(false, object); - } - - /** - * An object with an enumeration. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndBinaryUuid extends AutoKeyAndVal { - @DynamoDbScalarAttribute(type = ScalarAttributeType.B) - public UUID getVal() { - return super.getVal(); - } - - @Override - public void setVal(final UUID val) { - super.setVal(val); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ScanIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ScanIntegrationTest.java deleted file mode 100644 index 2c1a5a8515b6..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/ScanIntegrationTest.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.awscore.exception.AwsServiceException; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbScanExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.PaginatedParallelScanList; -import software.amazon.awssdk.services.dynamodb.datamodeling.PaginatedScanList; -import software.amazon.awssdk.services.dynamodb.datamodeling.ScanResultPage; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -/** - * Integration tests for the scan operation on DynamoDBMapper. - */ -public class ScanIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String TABLE_NAME = "aws-java-sdk-util-scan"; - /** - * We set a small limit in order to test the behavior of PaginatedList - * when it could not load all the scan result in one batch. - */ - private static final int SCAN_LIMIT = 10; - private static final int PARALLEL_SCAN_SEGMENTS = 5; - - private static void createTestData() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (int i = 0; i < 500; i++) { - util.save(new SimpleClass(Integer.toString(i), Integer.toString(i))); - } - } - - @BeforeClass - public static void setUpTestData() throws Exception { - String keyName = "id"; - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_NAME) - .keySchema(KeySchemaElement.builder().attributeName(keyName).keyType(KeyType.HASH).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(keyName).attributeType( - ScalarAttributeType.S).build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(10L) - .writeCapacityUnits(5L).build()) - .build(); - - TableUtils.createTableIfNotExists(dynamo, createTableRequest); - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - - createTestData(); - } - - - @Test - public void testScan() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression().withLimit(SCAN_LIMIT); - scanExpression - .addFilterCondition("value", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - scanExpression - .addFilterCondition("extraData", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - List list = util.scan(SimpleClass.class, scanExpression); - - int count = 0; - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - } - - int totalCount = util.count(SimpleClass.class, scanExpression); - - assertNotNull(list.get(totalCount / 2)); - assertEquals(totalCount, count); - assertEquals(totalCount, list.size()); - - assertTrue(list.contains(list.get(list.size() / 2))); - assertEquals(count, list.toArray().length); - } - - /** - * Tests scanning the table with AND/OR logic operator. - */ - @Test - public void testScanWithConditionalOperator() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression() - .withLimit(SCAN_LIMIT) - .withScanFilter(ImmutableMap.of( - "value", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL).build(), - "non-existent-field", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL).build() - )) - .withConditionalOperator(ConditionalOperator.AND); - - List andConditionResult = mapper.scan(SimpleClass.class, scanExpression); - assertTrue(andConditionResult.isEmpty()); - - List orConditionResult = mapper.scan(SimpleClass.class, - scanExpression.withConditionalOperator(ConditionalOperator.OR)); - assertFalse(orConditionResult.isEmpty()); - } - - @Test - public void testParallelScan() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression().withLimit(SCAN_LIMIT); - scanExpression - .addFilterCondition("value", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - scanExpression - .addFilterCondition("extraData", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - - PaginatedParallelScanList parallelScanList = util - .parallelScan(SimpleClass.class, scanExpression, PARALLEL_SCAN_SEGMENTS); - int count = 0; - Iterator iterator = parallelScanList.iterator(); - HashMap allDataAppearance = new HashMap(); - for (int i = 0; i < 500; i++) { - allDataAppearance.put("" + i, false); - } - while (iterator.hasNext()) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - allDataAppearance.put(next.getId(), true); - } - assertFalse(allDataAppearance.values().contains(false)); - - int totalCount = util.count(SimpleClass.class, scanExpression); - - assertNotNull(parallelScanList.get(totalCount / 2)); - assertEquals(totalCount, count); - assertEquals(totalCount, parallelScanList.size()); - - assertTrue(parallelScanList.contains(parallelScanList.get(parallelScanList.size() / 2))); - assertEquals(count, parallelScanList.toArray().length); - - } - - @Test - public void testParallelScanPerformance() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression().withLimit(SCAN_LIMIT); - scanExpression - .addFilterCondition("value", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - scanExpression - .addFilterCondition("extraData", Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - - long startTime = System.currentTimeMillis(); - PaginatedScanList scanList = util.scan(SimpleClass.class, scanExpression); - scanList.loadAllResults(); - long fullTableScanTime = System.currentTimeMillis() - startTime; - startTime = System.currentTimeMillis(); - PaginatedParallelScanList parallelScanList = util - .parallelScan(SimpleClass.class, scanExpression, PARALLEL_SCAN_SEGMENTS); - parallelScanList.loadAllResults(); - long parallelScanTime = System.currentTimeMillis() - startTime; - assertEquals(scanList.size(), parallelScanList.size()); - assertTrue(fullTableScanTime > parallelScanTime); - System.out.println("fullTableScanTime : " + fullTableScanTime + "(ms), parallelScanTime : " + parallelScanTime + "(ms)."); - } - - @Test - public void testParallelScanExceptionHandling() { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - int INVALID_LIMIT = 0; - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression().withLimit(INVALID_LIMIT); - try { - PaginatedParallelScanList parallelScanList = util - .parallelScan(SimpleClass.class, scanExpression, PARALLEL_SCAN_SEGMENTS); - fail("Should have seen the SdkServiceException"); - } catch (AwsServiceException exception) { - assertNotNull(exception.awsErrorDetails().errorCode()); - assertNotNull(exception.getMessage()); - } catch (Exception e) { - fail("Should have seen the SdkServiceException"); - } - - } - - @Test - public void testScanPage() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - DynamoDbScanExpression scanExpression = new DynamoDbScanExpression(); - scanExpression.addFilterCondition("value", - Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - scanExpression.addFilterCondition("extraData", - Condition.builder().comparisonOperator(ComparisonOperator.NOT_NULL.toString()).build()); - int limit = 3; - scanExpression.setLimit(limit); - ScanResultPage result = util.scanPage(SimpleClass.class, scanExpression); - - int count = 0; - Iterator iterator = result.getResults().iterator(); - Set seen = new HashSet(); - while (iterator.hasNext()) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - assertTrue(seen.add(next)); - } - - assertEquals(limit, count); - assertEquals(count, result.getResults().toArray().length); - - scanExpression.setExclusiveStartKey(result.lastEvaluatedKey()); - result = util.scanPage(SimpleClass.class, scanExpression); - - iterator = result.getResults().iterator(); - count = 0; - while (iterator.hasNext()) { - count++; - SimpleClass next = iterator.next(); - assertNotNull(next.getExtraData()); - assertNotNull(next.getValue()); - assertTrue(seen.add(next)); - } - - assertEquals(limit, count); - assertEquals(count, result.getResults().toArray().length); - - } - - @DynamoDbTable(tableName = "aws-java-sdk-util-scan") - public static final class SimpleClass { - private String id; - private String value; - private String extraData; - - - public SimpleClass() { - } - - public SimpleClass(String id, String value) { - this.id = id; - this.value = value; - this.extraData = UUID.randomUUID().toString(); - } - - @DynamoDbHashKey - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public String getExtraData() { - return extraData; - } - - public void setExtraData(String extraData) { - this.extraData = extraData; - } - } -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/SimpleNumericAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/SimpleNumericAttributesIntegrationTest.java deleted file mode 100644 index 8a2f023bcde6..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/SimpleNumericAttributesIntegrationTest.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; - -/** - * Tests numeric attributes - */ -public class SimpleNumericAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String INT_ATTRIBUTE = "intAttribute"; - private static final String INTEGER_ATTRIBUTE = "integerAttribute"; - private static final String FLOAT_ATTRIBUTE = "floatAttribute"; - private static final String FLOAT_OBJECT_ATTRIBUTE = "floatObjectAttribute"; - private static final String DOUBLE_ATTRIBUTE = "doubleAttribute"; - private static final String DOUBLE_OBJECT_ATTRIBUTE = "doubleObjectAttribute"; - private static final String BIG_INTEGER_ATTRIBUTE = "bigIntegerAttribute"; - private static final String BIG_DECIMAL_ATTRIBUTE = "bigDecimalAttribute"; - private static final String LONG_ATTRIBUTE = "longAttribute"; - private static final String LONG_OBJECT_ATTRIBUTE = "longObjectAttribute"; - private static final String BYTE_ATTRIBUTE = "byteAttribute"; - private static final String BYTE_OBJECT_ATTRIBUTE = "byteObjectAttribute"; - private static final String BOOLEAN_ATTRIBUTE = "booleanAttribute"; - private static final String BOOLEAN_OBJECT_ATTRIBUTE = "booleanObjectAttribute"; - private static final String SHORT_ATTRIBUTE = "shortAttribute"; - private static final String SHORT_OBJECT_ATTRIBUTE = "shortObjectAttribute"; - private static final List> attrs = new LinkedList>(); - // We don't start with the current system millis like other tests because - // it's out of the range of some data types - private static int start = 1; - private static int byteStart = -127; - - // Test data - static { - for (int i = 0; i < 5; i++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().s("" + start++).build()); - attr.put(INT_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(INTEGER_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(FLOAT_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(FLOAT_OBJECT_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(DOUBLE_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(DOUBLE_OBJECT_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(BIG_INTEGER_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(BIG_DECIMAL_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(LONG_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(LONG_OBJECT_ATTRIBUTE, AttributeValue.builder().n("" + start++).build()); - attr.put(BYTE_ATTRIBUTE, AttributeValue.builder().n("" + byteStart++).build()); - attr.put(BYTE_OBJECT_ATTRIBUTE, AttributeValue.builder().n("" + byteStart++).build()); - attr.put(BOOLEAN_ATTRIBUTE, AttributeValue.builder().n(start++ % 2 == 0 ? "1" : "0").build()); - attr.put(BOOLEAN_OBJECT_ATTRIBUTE, AttributeValue.builder().n(start++ % 2 == 0 ? "1" : "0").build()); - attr.put(SHORT_ATTRIBUTE, AttributeValue.builder().n("" + byteStart++).build()); - attr.put(SHORT_OBJECT_ATTRIBUTE, AttributeValue.builder().n("" + byteStart++).build()); - attrs.add(attr); - } - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperIntegrationTestBase.setUp(); - - // Insert the data - for (Map attr : attrs) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(attr).build()); - } - } - - private NumberAttributeClass getKeyObject(String key) { - NumberAttributeClass obj = new NumberAttributeClass(); - obj.setKey(key); - return obj; - } - - @Test - public void testLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - for (Map attr : attrs) { - NumberAttributeClass x = util.load(getKeyObject(attr.get(KEY_NAME).s())); - assertEquals(x.getKey(), attr.get(KEY_NAME).s()); - - // Convert all numbers to the most inclusive type for easy comparison - assertEquals(x.getBigDecimalAttribute(), new BigDecimal(attr.get(BIG_DECIMAL_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getBigIntegerAttribute()), new BigDecimal(attr.get(BIG_INTEGER_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getFloatAttribute()), new BigDecimal(attr.get(FLOAT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getFloatObjectAttribute()), new BigDecimal(attr.get(FLOAT_OBJECT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getDoubleAttribute()), new BigDecimal(attr.get(DOUBLE_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getDoubleObjectAttribute()), new BigDecimal(attr.get(DOUBLE_OBJECT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getIntAttribute()), new BigDecimal(attr.get(INT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getIntegerAttribute()), new BigDecimal(attr.get(INTEGER_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getLongAttribute()), new BigDecimal(attr.get(LONG_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getLongObjectAttribute()), new BigDecimal(attr.get(LONG_OBJECT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getByteAttribute()), new BigDecimal(attr.get(BYTE_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getByteObjectAttribute()), new BigDecimal(attr.get(BYTE_OBJECT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getShortAttribute()), new BigDecimal(attr.get(SHORT_ATTRIBUTE).n())); - assertEquals(new BigDecimal(x.getShortObjectAttribute()), new BigDecimal(attr.get(SHORT_OBJECT_ATTRIBUTE).n())); - assertEquals(x.isBooleanAttribute(), attr.get(BOOLEAN_ATTRIBUTE).n().equals("1")); - assertEquals(x.getBooleanObjectAttribute(), attr.get(BOOLEAN_OBJECT_ATTRIBUTE).n().equals("1")); - } - - // Test loading an object that doesn't exist - assertNull(util.load(getKeyObject("does not exist"))); - } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - NumberAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (NumberAttributeClass obj : objs) { - util.save(obj); - } - - for (NumberAttributeClass obj : objs) { - NumberAttributeClass loaded = util.load(obj); - loaded.setIgnored(obj.getIgnored()); - assertEquals(obj, loaded); - } - } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - NumberAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (NumberAttributeClass obj : objs) { - util.save(obj); - } - - for (NumberAttributeClass obj : objs) { - NumberAttributeClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - - NumberAttributeClass loadedObject = util.load(obj); - - // The ignored attribute isn't handled by big bird, so we have to - // set it manually here before doing the comparison. - assertFalse(replacement.getIgnored().equals(loadedObject.getIgnored())); - loadedObject.setIgnored(replacement.getIgnored()); - assertEquals(replacement, loadedObject); - } - } - - /** - * Tests automatically setting a hash key upon saving. - */ - @Test - public void testSetHashKey() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - NumberAttributeClass obj = getUniqueObject(); - obj.setKey(null); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (NumberAttributeClass obj : objs) { - assertNull(obj.getKey()); - util.save(obj); - assertNotNull(obj.getKey()); - NumberAttributeClass loadedObject = util.load(obj); - - // The ignored attribute isn't handled by big bird, so we have to - // set it manually here before doing the comparison. - assertFalse(obj.getIgnored().equals(loadedObject.getIgnored())); - loadedObject.setIgnored(obj.getIgnored()); - assertEquals(obj, loadedObject); - } - } - - @Test - public void testDelete() throws Exception { - NumberAttributeClass obj = getUniqueObject(); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(obj); - - NumberAttributeClass loaded = util.load(NumberAttributeClass.class, obj.getKey()); - loaded.setIgnored(obj.getIgnored()); - assertEquals(obj, loaded); - - util.delete(obj); - assertNull(util.load(NumberAttributeClass.class, obj.getKey())); - } - - @Test - public void performanceTest() throws Exception { - NumberAttributeClass obj = getUniqueObject(); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - - GetItemResponse item = dynamo.getItem(GetItemRequest.builder().tableName("aws-java-sdk-util").key( - mapKey(KEY_NAME, AttributeValue.builder().s(obj.getKey()).build())).build()); - - long start = System.currentTimeMillis(); - for (int i = 0; i < 10000; i++) { - mapper.marshallIntoObject(NumberAttributeClass.class, item.item()); - } - - long end = System.currentTimeMillis(); - - System.err.println("time: " + (end - start)); - } - - private NumberAttributeClass getUniqueObject() { - NumberAttributeClass obj = new NumberAttributeClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setBigDecimalAttribute(new BigDecimal(startKey++)); - obj.setBigIntegerAttribute(new BigInteger("" + startKey++)); - obj.setByteAttribute((byte) byteStart++); - obj.setByteObjectAttribute(new Byte("" + byteStart++)); - obj.setDoubleAttribute(new Double("" + start++)); - obj.setDoubleObjectAttribute(new Double("" + start++)); - obj.setFloatAttribute(new Float("" + start++)); - obj.setFloatObjectAttribute(new Float("" + start++)); - obj.setIntAttribute(new Integer("" + start++)); - obj.setIntegerAttribute(new Integer("" + start++)); - obj.setLongAttribute(new Long("" + start++)); - obj.setLongObjectAttribute(new Long("" + start++)); - obj.setShortAttribute(new Short("" + start++)); - obj.setShortObjectAttribute(new Short("" + start++)); - obj.setDateAttribute(new Date(startKey++)); - obj.setBooleanAttribute(start++ % 2 == 0); - obj.setBooleanObjectAttribute(start++ % 2 == 0); - obj.setIgnored("" + start++); - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(new Date(startKey++)); - obj.setCalendarAttribute(cal); - return obj; - } - - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/SimpleStringAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/SimpleStringAttributesIntegrationTest.java deleted file mode 100644 index f65fff6ffd7a..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/SimpleStringAttributesIntegrationTest.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.ConsistentRead; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.pojos.StringAttributeClass; - -/** - * Tests simple string attributes - */ -public class SimpleStringAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; - private static final String STRING_ATTRIBUTE = "stringAttribute"; - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for (int i = 0; i < 5; i++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().s("" + startKey++).build()); - attr.put(STRING_ATTRIBUTE, AttributeValue.builder().s("" + startKey++).build()); - attr.put(ORIGINAL_NAME_ATTRIBUTE, AttributeValue.builder().s("" + startKey++).build()); - attrs.add(attr); - } - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperIntegrationTestBase.setUp(); - - // Insert the data - for (Map attr : attrs) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(attr).build()); - } - } - - @Test - public void testLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - for (Map attr : attrs) { - StringAttributeClass x = util.load(StringAttributeClass.class, attr.get(KEY_NAME).s()); - assertEquals(x.getKey(), attr.get(KEY_NAME).s()); - assertEquals(x.getStringAttribute(), attr.get(STRING_ATTRIBUTE).s()); - assertEquals(x.getRenamedAttribute(), attr.get(ORIGINAL_NAME_ATTRIBUTE).s()); - } - - } - - @Test - public void testSave() { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - StringAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (StringAttributeClass obj : objs) { - util.save(obj); - } - - for (StringAttributeClass obj : objs) { - StringAttributeClass loaded = util.load(StringAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - /** - * Tests saving an incomplete object into DynamoDB - */ - @Test - public void testIncompleteObject() { - StringAttributeClass obj = getUniqueObject(); - obj.setStringAttribute(null); - DynamoDbMapper util = new DynamoDbMapper(dynamo); - util.save(obj); - - assertEquals(obj, util.load(StringAttributeClass.class, obj.getKey())); - - // test removing an attribute - assertNotNull(obj.getRenamedAttribute()); - obj.setRenamedAttribute(null); - util.save(obj); - assertEquals(obj, util.load(StringAttributeClass.class, obj.getKey())); - } - - @Test - public void testUpdate() { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - StringAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (StringAttributeClass obj : objs) { - util.save(obj); - } - - for (StringAttributeClass obj : objs) { - StringAttributeClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - - assertEquals(replacement, util.load(StringAttributeClass.class, obj.getKey())); - } - } - - @Test - public void testSaveOnlyKey() { - KeyOnly obj = new KeyOnly(); - obj.setKey("" + startKey++); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj); - - KeyOnly loaded = mapper.load(KeyOnly.class, obj.getKey(), new DynamoDbMapperConfig( - DynamoDbMapperConfig.ConsistentRead.CONSISTENT)); - assertEquals(obj, loaded); - - // saving again shouldn't be an error - mapper.save(obj); - } - - @Test - public void testSaveOnlyKeyClobber() { - KeyOnly obj = new KeyOnly(); - obj.setKey("" + startKey++); - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - mapper.save(obj, new DynamoDbMapperConfig(SaveBehavior.CLOBBER)); - - KeyOnly loaded = mapper.load(KeyOnly.class, obj.getKey(), new DynamoDbMapperConfig(ConsistentRead.CONSISTENT)); - assertEquals(obj, loaded); - - // saving again shouldn't be an error - mapper.save(obj, new DynamoDbMapperConfig(SaveBehavior.CLOBBER)); - } - - private StringAttributeClass getUniqueObject() { - StringAttributeClass obj = new StringAttributeClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setRenamedAttribute(String.valueOf(startKey++)); - obj.setStringAttribute(String.valueOf(startKey++)); - return obj; - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static final class KeyOnly { - private String key; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - KeyOnly other = (KeyOnly) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - return true; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/StringSetAttributesIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/StringSetAttributesIntegrationTest.java deleted file mode 100644 index 0033006f1c81..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/StringSetAttributesIntegrationTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.pojos.StringSetAttributeClass; - - -/** - * Tests string set attributes - */ -public class StringSetAttributesIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - private static final String ORIGINAL_NAME_ATTRIBUTE = "originalName"; - private static final String STRING_SET_ATTRIBUTE = "stringSetAttribute"; - private static final String EXTRA_ATTRIBUTE = "extra"; - private static final List> attrs = new LinkedList>(); - - // Test data - static { - for (int i = 0; i < 5; i++) { - Map attr = new HashMap(); - attr.put(KEY_NAME, AttributeValue.builder().s("" + startKey++).build()); - attr.put(STRING_SET_ATTRIBUTE, AttributeValue.builder().ss("" + ++startKey, "" + ++startKey, "" + ++startKey).build()); - attr.put(ORIGINAL_NAME_ATTRIBUTE, AttributeValue.builder().ss("" + ++startKey, "" + ++startKey, "" + ++startKey).build()); - attr.put(EXTRA_ATTRIBUTE, AttributeValue.builder().ss("" + ++startKey, "" + ++startKey, "" + ++startKey).build()); - attrs.add(attr); - } - } - - ; - - @BeforeClass - public static void setUp() throws Exception { - DynamoDBMapperIntegrationTestBase.setUp(); - - // Insert the data - for (Map attr : attrs) { - dynamo.putItem(PutItemRequest.builder().tableName(TABLE_NAME).item(attr).build()); - } - } - - @Test - public void testLoad() throws Exception { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - for (Map attr : attrs) { - StringSetAttributeClass x = util.load(StringSetAttributeClass.class, attr.get(KEY_NAME).s()); - assertEquals(x.getKey(), attr.get(KEY_NAME).s()); - assertSetsEqual(x.getStringSetAttribute(), toSet(attr.get(STRING_SET_ATTRIBUTE).ss())); - assertSetsEqual(x.getStringSetAttributeRenamed(), toSet(attr.get(ORIGINAL_NAME_ATTRIBUTE).ss())); - } - } - - /** - * Tests saving only some attributes of an object. - */ - @Test - public void testIncompleteObject() { - DynamoDbMapper util = new DynamoDbMapper(dynamo); - - StringSetAttributeClass obj = getUniqueObject(); - obj.setStringSetAttribute(null); - util.save(obj); - - assertEquals(obj, util.load(StringSetAttributeClass.class, obj.getKey())); - - obj.setStringSetAttributeRenamed(null); - util.save(obj); - assertEquals(obj, util.load(StringSetAttributeClass.class, obj.getKey())); - } - - @Test - public void testSave() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - StringSetAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (StringSetAttributeClass obj : objs) { - util.save(obj); - } - - for (StringSetAttributeClass obj : objs) { - StringSetAttributeClass loaded = util.load(StringSetAttributeClass.class, obj.getKey()); - assertEquals(obj, loaded); - } - } - - @Test - public void testUpdate() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - StringSetAttributeClass obj = getUniqueObject(); - objs.add(obj); - } - - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (StringSetAttributeClass obj : objs) { - util.save(obj); - } - - for (StringSetAttributeClass obj : objs) { - StringSetAttributeClass replacement = getUniqueObject(); - replacement.setKey(obj.getKey()); - util.save(replacement); - - assertEquals(replacement, util.load(StringSetAttributeClass.class, obj.getKey())); - } - } - - private StringSetAttributeClass getUniqueObject() { - StringSetAttributeClass obj = new StringSetAttributeClass(); - obj.setKey(String.valueOf(startKey++)); - obj.setStringSetAttribute(toSet(String.valueOf(startKey++), String.valueOf(startKey++), String.valueOf(startKey++))); - obj.setStringSetAttributeRenamed( - toSet(String.valueOf(startKey++), String.valueOf(startKey++), String.valueOf(startKey++))); - return obj; - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TableMapperIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TableMapperIntegrationTest.java deleted file mode 100644 index da07c518f590..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TableMapperIntegrationTest.java +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedTimestamp; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTableMapper; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; - -/** - * Tests updating component attribute fields correctly. - */ -public class TableMapperIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test using {@code Date}. - */ - @Test - public void testSaveIfNotExists() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - - mapper.saveIfNotExists(object); - } - - /** - * Test using {@code Date}. - */ - @Test(expected = ConditionalCheckFailedException.class) - public void testSaveIfNotExistsWhenExists() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - - mapper.saveIfNotExists(object); - mapper.saveIfNotExists(object); - } - - /** - * Test using {@code Date}. - */ - @Test - public void testSaveWhenExists() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - - mapper.saveIfNotExists(object); - mapper.save(object); - } - - /** - * Test using {@code Date}. - */ - @Test(expected = ConditionalCheckFailedException.class) - public void testSaveIfExistsWhenNotExists() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setKey(UUID.randomUUID().toString()); - - mapper.saveIfExists(object); - } - - /** - * Test using {@code Date}. - */ - @Test - public void testDeleteIfExistsWhenExists() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - - mapper.saveIfNotExists(object); - mapper.deleteIfExists(object); - } - - /** - * Test using {@code Date}. - */ - @Test(expected = ConditionalCheckFailedException.class) - public void testDeleteIfExistsWhenNotExists() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setKey(UUID.randomUUID().toString()); - - mapper.deleteIfExists(object); - } - - /** - * Test batch load with no results. - */ - @Test - public void testBatchLoadItemList() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object1 = new KeyAndDateValue(); - final KeyAndDateValue object2 = new KeyAndDateValue(); - - assertEquals(0, mapper.batchSave(Arrays.asList(object1, object2)).size()); - assertEquals(2, mapper.batchLoad(Arrays.asList(object1, object2)).size()); - assertEquals(0, mapper.batchDelete(Arrays.asList(object1, object2)).size()); - assertEquals(0, mapper.batchLoad(Arrays.asList(object1, object2)).size()); - } - - /** - * Test batch load with no results. - */ - @Test - public void testBatchLoadItemListOnNull() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - assertEquals(0, mapper.batchLoad((List) null).size()); - } - - /** - * Test batch load with no results. - */ - @Test - public void testBatchLoadItemListOnEmpty() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - assertEquals(0, mapper.batchLoad(Collections.emptyList()).size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryCount() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.count(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true))); - } - - /** - * Test a query. - */ - @Test - public void testQueryBeginsWith() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate") - .beginsWith(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryBetween() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate") - .between(object.getQueryDate(), object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryGreaterThanOrEqualTo() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").ge(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryGreaterThan() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(0, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").gt(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryEqualTo() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").eq(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryIn() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").in(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryIsNull() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(0, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate").isNull()) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryLessThanOrEqualTo() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").le(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryLessThan() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(0, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").lt(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryNotEqualTo() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(0, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", - mapper.field("queryDate").ne(object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryNotNull() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate").notNull()) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryAnyBetween() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate") - .betweenAny(object.getQueryDate(), object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryAnyBetweenLoNull() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate") - .betweenAny(null, object.getQueryDate())) - ).getResults().size()); - } - - /** - * Test a query. - */ - @Test - public void testQueryAnyBetweenHiNull() { - final DynamoDbTableMapper mapper = util.newTableMapper(KeyAndDateValue.class); - - final KeyAndDateValue object = new KeyAndDateValue(); - object.setQueryDate(new Date()); - - mapper.saveIfNotExists(object); - - assertEquals(1, mapper.queryPage(new DynamoDbQueryExpression() - .withHashKeyValues(object).withConsistentRead(true) - .withQueryFilterEntry("queryDate", mapper.field("queryDate") - .betweenAny(object.getQueryDate(), null)) - ).getResults().size()); - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateValue extends AutoKeyAndVal { - private Date queryDate; - - @DynamoDbAutoGeneratedTimestamp - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - - @DynamoDbAttribute(attributeName = "queryDate") - public Date getQueryDate() { - return this.queryDate; - } - - public void setQueryDate(final Date queryDate) { - this.queryDate = queryDate; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConvertedJsonIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConvertedJsonIntegrationTest.java deleted file mode 100644 index 55b483691315..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConvertedJsonIntegrationTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.List; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConvertedJson; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.Currency; - -/** - * Integration tests for {@code DynamoDBTypeConvertedJson}. - */ -public class TypeConvertedJsonIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test marshalling. - */ - @Test - public void testMarshalling() { - final KeyAndCurrency object = new KeyAndCurrency(); - object.setVal(new Currency(12.95D, "USD")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test marshalling a list. - */ - @Test - public void testListMarshalling() { - final KeyAndCurrencyList object = new KeyAndCurrencyList(); - object.setVal(new ArrayList()); - object.getVal().add(new Currency(1.99D, "CAD")); - object.getVal().add(new Currency(2.99D, "CAD")); - - final List after = assertBeforeAndAfterChange(false, object); - for (final Currency currency : after) { - assertNotNull(currency.getAmount()); - assertNotNull(currency.getUnit()); - } - } - - /** - * An object with a complex type. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCurrency extends AutoKeyAndVal { - @DynamoDbTypeConvertedJson - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - } - - /** - * An object with a complex type. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCurrencyList extends AutoKeyAndVal> { - @DynamoDbTypeConvertedJson(targetType = CurrencyListType.class) - public List getVal() { - return super.getVal(); - } - - @Override - public void setVal(final List val) { - super.setVal(val); - } - - public static final class CurrencyListType extends ArrayList { - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConvertedTimestampIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConvertedTimestampIntegrationTest.java deleted file mode 100644 index 4a1109a992ba..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConvertedTimestampIntegrationTest.java +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import java.time.format.DateTimeParseException; -import java.util.Calendar; -import java.util.Date; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConvertedTimestamp; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; - -/** - * Tests updating component attribute fields correctly. - */ -public class TypeConvertedTimestampIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test timestamp formatting. - */ - @Test - public void testCalendarTimestamp() throws Exception { - final KeyAndCalendarTimestamp object = new KeyAndCalendarTimestamp(); - object.setVal(Calendar.getInstance()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testCalendarTimestampNull() { - final KeyAndCalendarTimestamp object = new KeyAndCalendarTimestamp(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testDateTimestamp() throws Exception { - final KeyAndDateTimestamp object = new KeyAndDateTimestamp(); - object.setVal(Calendar.getInstance().getTime()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testDateTimestampNull() { - final KeyAndDateTimestamp object = new KeyAndDateTimestamp(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testLongTimestamp() throws Exception { - final KeyAndLongTimestamp object = new KeyAndLongTimestamp(); - object.setVal(Calendar.getInstance().getTime().getTime()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testLongTimestampNull() { - final KeyAndLongTimestamp object = new KeyAndLongTimestamp(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testEstCalendarTimestamp() throws Exception { - final KeyAndEstCalendarTimestamp object = new KeyAndEstCalendarTimestamp(); - object.setVal(Calendar.getInstance()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testEstDateTimestamp() { - final KeyAndEstDateTimestamp object = new KeyAndEstDateTimestamp(); - object.setVal(Calendar.getInstance().getTime()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test - public void testEstLongTimestamp() { - final KeyAndEstLongTimestamp object = new KeyAndEstLongTimestamp(); - object.setVal(Calendar.getInstance().getTime().getTime()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test(expected = DateTimeParseException.class) - public void testStringNotTimestamp() { - final KeyAndStringTimestamp object = new KeyAndStringTimestamp(); - object.setVal("NotTimestamp"); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test(expected = DynamoDbMappingException.class) - @Ignore("This behavior is different with the java.time classes because you can construct a formatter using an empty " - + "string as a pattern.") - public void testEmptyPattern() throws Exception { - final KeyAndEmptyPattern object = new KeyAndEmptyPattern(); - object.setVal(Calendar.getInstance().getTime()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test timestamp formatting. - */ - @Test(expected = DynamoDbMappingException.class) - public void testInvalidPattern() throws Exception { - final KeyAndInvalidPattern object = new KeyAndInvalidPattern(); - object.setVal(Calendar.getInstance().getTime()); - assertBeforeAndAfterChange(false, object); - } - - /** - * An object with {@code Calendar}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCalendarTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz") - public Calendar getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Calendar val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDateTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz") - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Long}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndLongTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz") - public Long getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Long val) { - super.setVal(val); - } - } - - /** - * An object with {@code Calendar}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndEstCalendarTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz", timeZone = "America/New_York") - public Calendar getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Calendar val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndEstDateTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz", timeZone = "America/New_York") - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Long}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndEstLongTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz", timeZone = "America/New_York") - public Long getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Long val) { - super.setVal(val); - } - } - - /** - * An object with {@code String}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndStringTimestamp extends AutoKeyAndVal { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz") - public String getVal() { - return super.getVal(); - } - - @Override - public void setVal(final String val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndEmptyPattern extends KeyAndDateTimestamp { - @DynamoDbTypeConvertedTimestamp(pattern = "") - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndEmptyTimeZone extends KeyAndDateTimestamp { - @DynamoDbTypeConvertedTimestamp(pattern = "yyyy MMddHHmmssSSSz", timeZone = "") - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - } - - /** - * An object with {@code Date}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndInvalidPattern extends KeyAndDateTimestamp { - @DynamoDbTypeConvertedTimestamp(pattern = "invalid") - public Date getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Date val) { - super.setVal(val); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConverterIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConverterIntegrationTest.java deleted file mode 100644 index e11f8a17dcdd..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypeConverterIntegrationTest.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConverted; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConverter; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.Currency; - -/** - * Tests updating component attribute fields correctly. - */ -public class TypeConverterIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test using {@code Currency}. - */ - @Test - public void testStringCurrency() { - final KeyAndStringCurrency object = new KeyAndStringCurrency(); - object.setVal(new Currency(79.99D, "CAD")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testStringCurrencyNull() { - final KeyAndStringCurrency object = new KeyAndStringCurrency(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test(expected = DynamoDbMappingException.class) //<- does not yet support lists/maps - public void testCurrency() { - final KeyAndCurrency object = new KeyAndCurrency(); - object.setVal(new Currency(69.99D, "CAD")); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testStringSetCurrency() { - final KeyAndStringSetCurrency object = new KeyAndStringSetCurrency(); - object.setVal(new HashSet(Arrays.asList(new Currency(4.99D, "USD"), new Currency(5.99D, "USD")))); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testStringSetCurrencyNull() { - final KeyAndStringSetCurrency object = new KeyAndStringSetCurrency(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testDoubleCurrency() { - final KeyAndDoubleCurrency object = new KeyAndDoubleCurrency(); - object.setVal(new Currency(99.99D, "CAD")); - - final Currency currency = assertBeforeAndAfterChange(null, object); - assertEquals(object.getVal().getAmount(), currency.getAmount()); - assertEquals("USD", currency.getUnit()); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testDoubleCurrencyNull() { - final KeyAndDoubleCurrency object = new KeyAndDoubleCurrency(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testDoubleSetCurrency() { - final KeyAndDoubleSetCurrency object = new KeyAndDoubleSetCurrency(); - object.setVal(new HashSet(Arrays.asList(new Currency(28.99D, "USD"), new Currency(29.99D, "USD")))); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test using {@code Currency}. - */ - @Test - public void testDoubleSetCurrencyNull() { - final KeyAndDoubleSetCurrency object = new KeyAndDoubleSetCurrency(); - assertBeforeAndAfterChange(false, object); - } - - /** - * An object with {@code Currency}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndStringCurrency extends AutoKeyAndVal { - @CurrencyFormat(separator = "-") - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - - @DynamoDbTypeConverted(converter = StringCurrencyConverter.class) - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD, ElementType.TYPE}) - public static @interface CurrencyFormat { - String separator() default " "; - } - - public static final class StringCurrencyConverter implements DynamoDbTypeConverter { - private final CurrencyFormat f; - - public StringCurrencyConverter(final Class targetType, final CurrencyFormat f) { - this.f = f; - } - - @Override - public String convert(final Currency object) { - return new StringBuilder().append(object.getAmount()).append(f.separator()).append(object.getUnit()).toString(); - } - - @Override - public Currency unconvert(final String object) { - final String[] splits = object.split(f.separator()); - return new Currency(Double.valueOf(splits[0]), splits[1]); - } - } - } - - /** - * An object with {@code Currency}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndCurrency extends AutoKeyAndVal { - @DynamoDbTypeConverted(converter = NoConvertCurrencyConverter.class) - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - - public static final class NoConvertCurrencyConverter implements DynamoDbTypeConverter { - @Override - public Currency convert(final Currency object) { - return object; - } - - @Override - public Currency unconvert(final Currency object) { - return object; - } - } - } - - /** - * An object with {@code Currency}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndStringSetCurrency extends AutoKeyAndVal> { - @CurrencyFormat(separator = "-") - public Set getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Set val) { - super.setVal(val); - } - - @DynamoDbTypeConverted(converter = StringSetCurrencyConverter.class) - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD, ElementType.TYPE}) - public static @interface CurrencyFormat { - String separator() default " "; - } - - public static final class StringSetCurrencyConverter implements DynamoDbTypeConverter, Set> { - private final CurrencyFormat f; - - public StringSetCurrencyConverter(final Class targetType, final CurrencyFormat f) { - this.f = f; - } - - @Override - public Set convert(final Set object) { - final Set objects = new HashSet(); - for (final Currency o : object) { - objects.add(new StringBuilder().append(o.getAmount()).append(f.separator()).append(o.getUnit()).toString()); - } - return objects; - } - - @Override - public Set unconvert(final Set object) { - final Set objects = new HashSet(); - for (final String o : object) { - final String[] splits = o.split(f.separator()); - objects.add(new Currency(Double.valueOf(splits[0]), splits[1])); - } - return objects; - } - } - } - - /** - * An object with {@code Currency}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDoubleCurrency extends AutoKeyAndVal { - @CurrencyFormat - public Currency getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Currency val) { - super.setVal(val); - } - - @DynamoDbTypeConverted(converter = DoubleCurrencyConverter.class) - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD, ElementType.TYPE}) - public static @interface CurrencyFormat { - String separator() default " "; - - String unit() default "USD"; - } - - public static final class DoubleCurrencyConverter implements DynamoDbTypeConverter { - private final CurrencyFormat f; - - public DoubleCurrencyConverter(final Class targetType, final CurrencyFormat f) { - this.f = f; - } - - @Override - public Double convert(final Currency object) { - return object.getAmount(); - } - - @Override - public Currency unconvert(final Double object) { - return new Currency(object, f.unit()); - } - } - } - - /** - * An object with {@code Currency}. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDoubleSetCurrency extends AutoKeyAndVal> { - @CurrencyFormat - public Set getVal() { - return super.getVal(); - } - - @Override - public void setVal(final Set val) { - super.setVal(val); - } - - @DynamoDbTypeConverted(converter = DoubleSetCurrencyConverter.class) - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.METHOD, ElementType.TYPE}) - public static @interface CurrencyFormat { - String separator() default " "; - - String unit() default "USD"; - } - - public static final class DoubleSetCurrencyConverter implements DynamoDbTypeConverter, Set> { - private final CurrencyFormat f; - - public DoubleSetCurrencyConverter(final Class targetType, final CurrencyFormat f) { - this.f = f; - } - - @Override - public Set convert(final Set object) { - final Set objects = new HashSet(); - for (final Currency o : object) { - objects.add(o.getAmount()); - } - return objects; - } - - @Override - public Set unconvert(final Set object) { - final Set objects = new HashSet(); - for (final Double o : object) { - objects.add(new Currency(o, f.unit())); - } - return objects; - } - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypedIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypedIntegrationTest.java deleted file mode 100644 index 926e84d39075..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/TypedIntegrationTest.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedDefault; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTyped; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; - -/** - * Status tests for {@code Typed}. - */ -public class TypedIntegrationTest extends AbstractKeyAndValIntegrationTestCase { - - /** - * Test the mappings. - */ - @Test - public void testMarshalling() { - final KeyAndBinaryUuid object = new KeyAndBinaryUuid(); - object.setVal(UUID.randomUUID()); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test with a null enum val. - */ - @Test - public void testNullEnumValue() { - final KeyAndStatus object = new KeyAndStatus(); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test with a non-null enum val. - */ - @Test - public void testEnumMarshalling() { - final KeyAndStatus object = new KeyAndStatus(); - object.setVal(KeyAndStatus.Status.Y); - assertBeforeAndAfterChange(false, object); - } - - /** - * Test with a null enum val. - */ - @Test - public void testDefaultEnumValue() { - final KeyAndDefaultStatus object = new KeyAndDefaultStatus(); - final KeyAndStatus.Status value = assertBeforeAndAfterChange(true, object); - assertEquals(KeyAndStatus.Status.Z, value); - } - - /** - * Test the mappings. - */ - @Test - public void testNativeMap() { - final Map map = new HashMap(); - map.put("A", AttributeValue.builder().n("123").build()); - - final KeyAndNativeValue object = new KeyAndNativeValue(); - object.setVal(AttributeValue.builder().m(map).build()); - assertBeforeAndAfterChange(false, object); - } - - /** - * test object. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndBinaryUuid extends AutoKeyAndVal { - @DynamoDbTyped(DynamoDbAttributeType.B) - public UUID getVal() { - return super.getVal(); - } - - @Override - public void setVal(final UUID val) { - super.setVal(val); - } - } - - /** - * An object with an enumeration. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndStatus extends AutoKeyAndVal { - @DynamoDbTyped(DynamoDbAttributeType.S) - public Status getVal() { - return super.getVal(); - } - - ; - - @Override - public void setVal(final Status val) { - super.setVal(val); - } - - public static enum Status { - X, - Y, - Z - } - } - - /** - * An object with an enumeration. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndDefaultStatus extends KeyAndStatus { - @DynamoDbAutoGeneratedDefault("Z") - @DynamoDbTyped(DynamoDbAttributeType.S) - public Status getVal() { - return super.getVal(); - } - } - - /** - * test object. - */ - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class KeyAndNativeValue extends AutoKeyAndVal { - @DynamoDbTyped(DynamoDbAttributeType.M) - public AttributeValue getVal() { - return super.getVal(); - } - - @Override - public void setVal(final AttributeValue val) { - super.setVal(val); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/VersionAttributeUpdateIntegrationTest.java b/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/VersionAttributeUpdateIntegrationTest.java deleted file mode 100644 index a41864745597..000000000000 --- a/test/dynamodbmapper-v1/src/it/java/software/amazon/awssdk/services/dynamodb/mapper/VersionAttributeUpdateIntegrationTest.java +++ /dev/null @@ -1,664 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.junit.Test; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.services.dynamodb.DynamoDBMapperIntegrationTestBase; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbDeleteExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbSaveExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbVersionAttribute; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -/** - * Tests updating version fields correctly - */ -public class VersionAttributeUpdateIntegrationTest extends DynamoDBMapperIntegrationTestBase { - - @Test(expected = DynamoDbMappingException.class) - public void testStringVersion() throws Exception { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - StringVersionField obj = getUniqueObject(new StringVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (StringVersionField obj : objs) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - assertEquals(obj, util.load(StringVersionField.class, obj.getKey())); - } - } - - @Test - public void testBigIntegerVersion() { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - BigIntegerVersionField obj = getUniqueObject(new BigIntegerVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (BigIntegerVersionField obj : objs) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - - assertEquals(obj, util.load(BigIntegerVersionField.class, obj.getKey())); - } - - for (BigIntegerVersionField obj : objs) { - BigIntegerVersionField replacement = getUniqueObject(new BigIntegerVersionField()); - replacement.setKey(obj.getKey()); - replacement.setVersion(obj.getVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getVersion().equals(replacement.getVersion())); - - BigIntegerVersionField loadedObject = util.load(BigIntegerVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - - // Now try again overlaying the correct version number by using a saveExpression - // this should not throw the conditional check failed exception - try { - DynamoDbSaveExpression saveExpression = new DynamoDbSaveExpression(); - Map expected = new HashMap(); - ExpectedAttributeValue expectedVersion = ExpectedAttributeValue.builder() - .value(AttributeValue.builder() - .n(obj.getVersion().add(BigInteger.valueOf(1)).toString()).build()).build(); - expected.put("version", expectedVersion); - saveExpression.setExpected(expected); - util.save(obj, saveExpression); - } catch (Exception expected) { - fail("This should succeed, version was updated."); - } - } - } - - @Test - public void testIntegerVersion() { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - IntegerVersionField obj = getUniqueObject(new IntegerVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (IntegerVersionField obj : objs) { - assertNull(obj.getNotCalledVersion()); - util.save(obj); - assertNotNull(obj.getNotCalledVersion()); - - assertEquals(obj, util.load(IntegerVersionField.class, obj.getKey())); - } - - for (IntegerVersionField obj : objs) { - IntegerVersionField replacement = getUniqueObject(new IntegerVersionField()); - replacement.setKey(obj.getKey()); - replacement.setNotCalledVersion(obj.getNotCalledVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getNotCalledVersion().equals(replacement.getNotCalledVersion())); - - IntegerVersionField loadedObject = util.load(IntegerVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - - // Trying to delete the object should also fail - try { - util.delete(obj); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - - // But specifying CLOBBER will allow deletion - util.save(obj, new DynamoDbMapperConfig(SaveBehavior.CLOBBER)); - - // Trying to delete with the wrong version should fail - try { - //version is now 2 in db, set object version to 3. - obj.setNotCalledVersion(3); - util.delete(obj); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - - // Now try deleting again overlaying the correct version number by using a deleteExpression - // this should not throw the conditional check failed exception - try { - DynamoDbDeleteExpression deleteExpression = new DynamoDbDeleteExpression(); - Map expected = new HashMap(); - ExpectedAttributeValue expectedVersion = ExpectedAttributeValue.builder() - .value(AttributeValue.builder() - .n("2").build()).build(); //version is still 2 in db - expected.put("version", expectedVersion); - deleteExpression.setExpected(expected); - util.delete(obj, deleteExpression); - } catch (Exception expected) { - fail("This should succeed, version was updated."); - } - } - } - - /** - * Tests providing additional expected conditions when saving and deleting - * item with versioned fields. - */ - @Test - public void testVersionedAttributeWithUserProvidedExpectedConditions() { - DynamoDbMapper mapper = new DynamoDbMapper(dynamo); - IntegerVersionField versionedObject = getUniqueObject(new IntegerVersionField()); - assertNull(versionedObject.getNotCalledVersion()); - - // Add additional expected conditions via DynamoDBSaveExpression. - // Expected conditions joined by AND are compatible with the conditions - // for auto-generated keys. - DynamoDbSaveExpression saveExpression = new DynamoDbSaveExpression() - .withExpected(Collections.singletonMap( - "otherAttribute", ExpectedAttributeValue.builder().exists(false).build())) - .withConditionalOperator(ConditionalOperator.AND); - // The save should succeed since the user provided conditions are joined by AND. - mapper.save(versionedObject, saveExpression); - // The version field should be populated - assertNotNull(versionedObject.getNotCalledVersion()); - IntegerVersionField other = mapper.load(IntegerVersionField.class, versionedObject.getKey()); - assertEquals(other, versionedObject); - - // delete should also work - DynamoDbDeleteExpression deleteExpression = new DynamoDbDeleteExpression() - .withExpected(Collections.singletonMap( - "otherAttribute", ExpectedAttributeValue.builder().exists(false).build())) - .withConditionalOperator(ConditionalOperator.AND); - mapper.delete(versionedObject, deleteExpression); - - // Change the conditional operator to OR. - // IllegalArgumentException is expected since the additional expected - // conditions cannot be joined with the conditions for auto-generated - // keys. - saveExpression.setConditionalOperator(ConditionalOperator.OR); - deleteExpression.setConditionalOperator(ConditionalOperator.OR); - try { - mapper.save(getUniqueObject(new IntegerVersionField()), saveExpression); - } catch (IllegalArgumentException expected) { - // Ignored or expected. - } - try { - mapper.delete(getUniqueObject(new IntegerVersionField()), deleteExpression); - } catch (IllegalArgumentException expected) { - // Ignored or expected. - } - - // User-provided OR conditions should work if they completely override - // the generated conditions for the version field. - Map goodConditions = - ImmutableMap.of( - "otherAttribute", ExpectedAttributeValue.builder().exists(false).build(), - "version", ExpectedAttributeValue.builder().exists(false).build() - ); - Map badConditions = - ImmutableMap.of( - "otherAttribute", ExpectedAttributeValue.builder().value(AttributeValue.builder().s("non-existent-value").build()).build(), - "version", ExpectedAttributeValue.builder().value(AttributeValue.builder().n("-1").build()).build() - ); - - IntegerVersionField newObj = getUniqueObject(new IntegerVersionField()); - saveExpression.setExpected(badConditions); - try { - mapper.save(newObj, saveExpression); - } catch (ConditionalCheckFailedException expected) { - // Ignored or expected. - } - - saveExpression.setExpected(goodConditions); - mapper.save(newObj, saveExpression); - - deleteExpression.setExpected(badConditions); - try { - mapper.delete(newObj, deleteExpression); - } catch (ConditionalCheckFailedException expected) { - // Ignored or expected. - } - - deleteExpression.setExpected(goodConditions); - mapper.delete(newObj, deleteExpression); - } - - @Test - public void testByteVersion() { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - ByteVersionField obj = getUniqueObject(new ByteVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (ByteVersionField obj : objs) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - - assertEquals(obj, util.load(ByteVersionField.class, obj.getKey())); - } - - for (ByteVersionField obj : objs) { - ByteVersionField replacement = getUniqueObject(new ByteVersionField()); - replacement.setKey(obj.getKey()); - replacement.setVersion(obj.getVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getVersion().equals(replacement.getVersion())); - - ByteVersionField loadedObject = util.load(ByteVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - } - } - - @Test - public void testLongVersion() { - List objs = new ArrayList(); - for (int i = 0; i < 5; i++) { - LongVersionField obj = getUniqueObject(new LongVersionField()); - objs.add(obj); - } - - // Saving new objects with a null version field should populate it - DynamoDbMapper util = new DynamoDbMapper(dynamo); - for (LongVersionField obj : objs) { - assertNull(obj.getVersion()); - util.save(obj); - assertNotNull(obj.getVersion()); - - assertEquals(obj, util.load(LongVersionField.class, obj.getKey())); - } - - for (LongVersionField obj : objs) { - LongVersionField replacement = getUniqueObject(new LongVersionField()); - replacement.setKey(obj.getKey()); - replacement.setVersion(obj.getVersion()); - - util.save(replacement); - // The version field should have changed in memory - assertFalse(obj.getVersion().equals(replacement.getVersion())); - - LongVersionField loadedObject = util.load(LongVersionField.class, obj.getKey()); - assertEquals(replacement, loadedObject); - - // Trying to update the object again should trigger a concurrency - // exception - try { - util.save(obj); - fail("Should have thrown an exception"); - } catch (Exception expected) { - // Ignored or expected. - } - } - } - - private T getUniqueObject(T obj) { - obj.setKey("" + startKey++); - obj.setNormalStringAttribute("" + startKey++); - return obj; - } - - @DynamoDbTable(tableName = "aws-java-sdk-util") - public static class VersionFieldBaseClass { - - protected String key; - protected String normalStringAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute - public String getNormalStringAttribute() { - return normalStringAttribute; - } - - public void setNormalStringAttribute(String normalStringAttribute) { - this.normalStringAttribute = normalStringAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((normalStringAttribute == null) ? 0 : normalStringAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - VersionFieldBaseClass other = (VersionFieldBaseClass) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (normalStringAttribute == null) { - if (other.normalStringAttribute != null) { - return false; - } - } else if (!normalStringAttribute.equals(other.normalStringAttribute)) { - return false; - } - return true; - } - } - - public static class StringVersionField extends VersionFieldBaseClass { - - private String version; - - @DynamoDbVersionAttribute - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - StringVersionField other = (StringVersionField) obj; - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - } - - public static class BigIntegerVersionField extends VersionFieldBaseClass { - - private BigInteger version; - - @DynamoDbVersionAttribute - public BigInteger getVersion() { - return version; - } - - public void setVersion(BigInteger version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - BigIntegerVersionField other = (BigIntegerVersionField) obj; - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "BigIntegerVersionField [version=" + version + ", key=" + key + ", normalStringAttribute=" - + normalStringAttribute + "]"; - } - } - - public static final class IntegerVersionField extends VersionFieldBaseClass { - - private Integer notCalledVersion; - - // Making sure that we can substitute attribute names as necessary - @DynamoDbVersionAttribute(attributeName = "version") - public Integer getNotCalledVersion() { - return notCalledVersion; - } - - public void setNotCalledVersion(Integer getNotCalledVersion) { - this.notCalledVersion = getNotCalledVersion; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((notCalledVersion == null) ? 0 : notCalledVersion.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - IntegerVersionField other = (IntegerVersionField) obj; - if (notCalledVersion == null) { - if (other.notCalledVersion != null) { - return false; - } - } else if (!notCalledVersion.equals(other.notCalledVersion)) { - return false; - } - return true; - } - } - - public static final class ByteVersionField extends VersionFieldBaseClass { - - private Byte version; - - @DynamoDbVersionAttribute - public Byte getVersion() { - return version; - } - - public void setVersion(Byte version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - ByteVersionField other = (ByteVersionField) obj; - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - } - - public static final class LongVersionField extends VersionFieldBaseClass { - - private Long version; - - @DynamoDbVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!super.equals(obj)) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - LongVersionField other = (LongVersionField) obj; - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/ConvenientMapSetterTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/ConvenientMapSetterTest.java deleted file mode 100644 index c10c5a41bcbd..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/ConvenientMapSetterTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import org.junit.Test; - -/** - * Tests on using convenient map setters. - */ -public class ConvenientMapSetterTest { - - /** Test on using map entry adder method. */ - @Test - public void testMapEntryAdderMethod() { -// NOTE(dongie): Convenience setters are not generated -// PutItemRequest putItemRequest = new PutItemRequest() -// .addItemEntry("hash-key", AttributeValue.builder().withS("1")) -// .addItemEntry("range-key", AttributeValue.builder().withS("2")) -// .addItemEntry("attribute", AttributeValue.builder().withS("3")); -// -// Map item = putItemRequest.getItem(); -// assertEquals(3, item.size()); -// assertEquals("1", item.get("hash-key").s()); -// assertEquals("2", item.get("range-key").s()); -// assertEquals("3", item.get("attribute").s()); -// -// putItemRequest.clearItemEntries(); -// assertNull(putItemRequest.getItem()); -// } -// -// /** Test on using predefined map entry setter to provide map parameter. */ -// @Test -// public void testPredefinedMapEntryMethod() { -// ScanRequest scanRequest = new ScanRequest().withExclusiveStartKey( -// new AbstractMap.SimpleEntry("hash-key", AttributeValue.builder().withS("1")), -// new AbstractMap.SimpleEntry("range-key", AttributeValue.builder().withS("2"))); -// -// Map item = scanRequest.getExclusiveStartKey(); -// assertEquals(2, item.size()); -// assertEquals("1", item.get("hash-key").s()); -// assertEquals("2", item.get("range-key").s()); -// } -// -// /** Test on IllegalArgumentException when providing duplicated keys. */ -// @Test(expected = IllegalArgumentException.class) -// public void testDuplicatedKeysException() { -// new PutItemRequest() -// .addItemEntry("hash-key", AttributeValue.builder().withS("1")) -// .addItemEntry("hash-key", AttributeValue.builder().withS("2")); -// } -// -// /** Test on handling null entry objects. */ -// @Test -// public void testNullEntryException() { -// // hashKey is set as not nullable, and rangeKey is nullable -// // so this call should be fine. -// ScanRequest scanRequest = new ScanRequest().withExclusiveStartKey( -// new AbstractMap.SimpleEntry("hash-key", AttributeValue.builder().withS("1")), -// null); -// -// // but this call should throw IllegalArgumentException. -// try { -// scanRequest.withExclusiveStartKey( -// null, -// new AbstractMap.SimpleEntry("hash-key", AttributeValue.builder().withS("1"))); -// fail("Should throw IllegalArgumentException."); -// } catch (IllegalArgumentException iae) { -// // Ignored or expected. -// } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/ImmutableObjectUtils.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/ImmutableObjectUtils.java deleted file mode 100644 index 4139283ae24d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/ImmutableObjectUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import java.util.Arrays; -import software.amazon.awssdk.annotations.SdkProtectedApi; - -@SdkProtectedApi -public final class ImmutableObjectUtils { - - private ImmutableObjectUtils() { - } - - public static void setObjectMember(Object o, String memberName, T value) { - Arrays.stream(o.getClass().getDeclaredFields()) - .filter(f -> f.getName().equals(memberName)) - .findFirst() - .ifPresent(f -> { - f.setAccessible(true); - try { - f.set(o, value); - } catch (IllegalAccessException e) { - throw new RuntimeException("Unable to reflectively set member " + memberName); - } - }); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/TypeConvertedJsonTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/TypeConvertedJsonTest.java deleted file mode 100644 index f612e462af29..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/TypeConvertedJsonTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.when; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConvertedJson; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; - -@RunWith(MockitoJUnitRunner.class) -public class TypeConvertedJsonTest { - - private static final String HASH_KEY = "1234"; - - @Mock - private DynamoDbClient ddb; - - @Test - public void responseWithUnmappedField_IgnoresUnknownFieldAndUnmarshallsCorrectly() { - final DynamoDbMapper mapper = new DynamoDbMapper(ddb); - when(ddb.getItem(any(GetItemRequest.class))) - .thenReturn(GetItemResponse.builder().item( - ImmutableMap.of("hashKey", AttributeValue.builder().s(HASH_KEY).build(), - "jsonMappedPojo", AttributeValue.builder().s( - "{\"knownField\": \"knownValue\", \"unknownField\": \"unknownValue\"}").build() - )).build()); - - final TopLevelPojo pojo = mapper.load(new TopLevelPojo().setHashKey(HASH_KEY)); - assertEquals("knownValue", pojo.getJsonMappedPojo().getKnownField()); - } - - @DynamoDbTable(tableName = "TestTable") - public static class TopLevelPojo { - - @DynamoDbHashKey - private String hashKey; - - @DynamoDbTypeConvertedJson - private JsonMappedPojo jsonMappedPojo; - - public String getHashKey() { - return hashKey; - } - - public TopLevelPojo setHashKey(String hashKey) { - this.hashKey = hashKey; - return this; - } - - public JsonMappedPojo getJsonMappedPojo() { - return jsonMappedPojo; - } - - public void setJsonMappedPojo(JsonMappedPojo jsonMappedPojo) { - this.jsonMappedPojo = jsonMappedPojo; - } - } - - public static class JsonMappedPojo { - - private String knownField; - - public String getKnownField() { - return knownField; - } - - public void setKnownField(String knownField) { - this.knownField = knownField; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AbstractDynamoDbMapper.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AbstractDynamoDbMapper.java deleted file mode 100644 index 0956648cab9f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AbstractDynamoDbMapper.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper.FailedBatch; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; - -/** - * Abstract implementation of {@code IDynamoDBMapper}. Convenient method forms pass through to the - * corresponding overload that takes a request object, which throws an - * {@code UnsupportedOperationException}. - */ -public class AbstractDynamoDbMapper implements IDynamoDbMapper { - - private final DynamoDbMapperConfig config; - - protected AbstractDynamoDbMapper(final DynamoDbMapperConfig defaults) { - this.config = DynamoDbMapperConfig.DEFAULT.merge(defaults); - } - - protected AbstractDynamoDbMapper() { - this(DynamoDbMapperConfig.DEFAULT); - } - - protected final String getTableName(Class clazz, Object object, DynamoDbMapperConfig config) { - if (object != null && config.getObjectTableNameResolver() != null) { - return config.getObjectTableNameResolver().getTableName(object, config); - } - return getTableName(clazz, config); - } - - protected final String getTableName(Class clazz, DynamoDbMapperConfig config) { - if (config.getTableNameResolver() == null) { - return DynamoDbMapperConfig.DefaultTableNameResolver.INSTANCE.getTableName(clazz, config); - } - return config.getTableNameResolver().getTableName(clazz, config); - } - - protected final DynamoDbMapperConfig mergeConfig(DynamoDbMapperConfig overrides) { - return this.config.merge(overrides); - } - - @Override - public DynamoDbMapperTableModel getTableModel(Class clazz) { - return getTableModel(clazz, config); - } - - @Override - public DynamoDbMapperTableModel getTableModel(Class clazz, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public T load(Class clazz, Object hashKey, DynamoDbMapperConfig config) { - return load(clazz, hashKey, (Object) null, config); - } - - @Override - public T load(Class clazz, Object hashKey) { - return load(clazz, hashKey, (Object) null, config); - } - - @Override - public T load(Class clazz, Object hashKey, Object rangeKey) { - return load(clazz, hashKey, rangeKey, config); - } - - @Override - public T load(Class clazz, Object hashKey, Object rangeKey, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public T load(T keyObject) { - return load(keyObject, config); - } - - @Override - public T load(T keyObject, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public T marshallIntoObject(Class clazz, Map itemAttributes) { - return marshallIntoObject(clazz, itemAttributes, config); - } - - public T marshallIntoObject(Class clazz, Map itemAttributes, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public List marshallIntoObjects(Class clazz, List> itemAttributes) { - return marshallIntoObjects(clazz, itemAttributes, config); - } - - public List marshallIntoObjects(Class clazz, List> itemAttributes, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public void save(T object) { - save(object, (DynamoDbSaveExpression) null, config); - } - - @Override - public void save(T object, DynamoDbSaveExpression saveExpression) { - save(object, saveExpression, config); - } - - @Override - public void save(T object, DynamoDbMapperConfig config) { - save(object, (DynamoDbSaveExpression) null, config); - } - - @Override - public void save(T object, DynamoDbSaveExpression saveExpression, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public void delete(Object object) { - delete(object, (DynamoDbDeleteExpression) null, config); - } - - @Override - public void delete(Object object, DynamoDbDeleteExpression deleteExpression) { - delete(object, deleteExpression, config); - } - - @Override - public void delete(Object object, DynamoDbMapperConfig config) { - delete(object, (DynamoDbDeleteExpression) null, config); - } - - @Override - public void delete(T object, DynamoDbDeleteExpression deleteExpression, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public List batchDelete(Iterable objectsToDelete) { - return batchWrite(Collections.emptyList(), objectsToDelete, config); - } - - @Override - public List batchDelete(Object... objectsToDelete) { - return batchWrite(Collections.emptyList(), Arrays.asList(objectsToDelete), config); - } - - @Override - public List batchSave(Iterable objectsToSave) { - return batchWrite(objectsToSave, Collections.emptyList(), config); - } - - @Override - public List batchSave(Object... objectsToSave) { - return batchWrite(Arrays.asList(objectsToSave), Collections.emptyList(), config); - } - - @Override - public List batchWrite(Iterable objectsToWrite, - Iterable objectsToDelete) { - return batchWrite(objectsToWrite, objectsToDelete, config); - } - - @Override - public List batchWrite(Iterable objectsToWrite, - Iterable objectsToDelete, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public Map> batchLoad(Iterable itemsToGet) { - return batchLoad(itemsToGet, config); - } - - @Override - public Map> batchLoad(Iterable itemsToGet, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public Map> batchLoad(Map, List> itemsToGet) { - return batchLoad(itemsToGet, config); - } - - @Override - public Map> batchLoad(Map, List> itemsToGet, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public PaginatedScanList scan(Class clazz, DynamoDbScanExpression scanExpression) { - return scan(clazz, scanExpression, config); - } - - @Override - public PaginatedScanList scan(Class clazz, - DynamoDbScanExpression scanExpression, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public PaginatedParallelScanList parallelScan(Class clazz, - DynamoDbScanExpression scanExpression, - int totalSegments) { - return parallelScan(clazz, scanExpression, totalSegments, config); - } - - @Override - public PaginatedParallelScanList parallelScan(Class clazz, - DynamoDbScanExpression scanExpression, - int totalSegments, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public ScanResultPage scanPage(Class clazz, DynamoDbScanExpression scanExpression) { - return scanPage(clazz, scanExpression, config); - } - - @Override - public ScanResultPage scanPage(Class clazz, - DynamoDbScanExpression scanExpression, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public int count(Class clazz, DynamoDbScanExpression scanExpression) { - return count(clazz, scanExpression, config); - } - - @Override - public int count(Class clazz, DynamoDbScanExpression scanExpression, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public int count(Class clazz, DynamoDbQueryExpression queryExpression) { - return count(clazz, queryExpression, config); - } - - @Override - public int count(Class clazz, DynamoDbQueryExpression queryExpression, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public PaginatedQueryList query(Class clazz, DynamoDbQueryExpression queryExpression) { - return query(clazz, queryExpression, config); - } - - @Override - public PaginatedQueryList query(Class clazz, - DynamoDbQueryExpression queryExpression, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public QueryResultPage queryPage(Class clazz, DynamoDbQueryExpression queryExpression) { - return queryPage(clazz, queryExpression, config); - } - - @Override - public QueryResultPage queryPage(Class clazz, - DynamoDbQueryExpression queryExpression, - DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public S3ClientCache s3ClientCache() { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public S3Link createS3Link(String bucketName, String key) { - return createS3Link((Region) null, bucketName, key); - } - - @Override - public S3Link createS3Link(Region s3region, String bucketName, String key) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public S3Link createS3Link(String s3region, String bucketName, String key) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public CreateTableRequest generateCreateTableRequest(Class clazz) { - return generateCreateTableRequest(clazz, config); - } - - public CreateTableRequest generateCreateTableRequest(Class clazz, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - - @Override - public DeleteTableRequest generateDeleteTableRequest(Class clazz) { - return generateDeleteTableRequest(clazz, config); - } - - public DeleteTableRequest generateDeleteTableRequest(Class clazz, DynamoDbMapperConfig config) { - throw new UnsupportedOperationException("operation not supported in " + getClass()); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AbstractEnumMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AbstractEnumMarshaller.java deleted file mode 100644 index 0bca5de2c7b3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AbstractEnumMarshaller.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static software.amazon.awssdk.core.internal.util.ThrowableUtils.failure; - -/** - * Generic marshaller for enumerations. - * - * Please note, there are some risks in distributed systems when using - * enumerations as attributes intead of simply using a String. - * When adding new values to the enumeration, the enum only changes must - * be deployed before the enumeration value can be persisted. This will - * ensure that all systems have the correct code to map it from the item - * record in DynamoDB to your objects. - * - * @see DynamoDbMarshaller - * - * @deprecated Replaced by {@link DynamoDbTypeConvertedEnum} - */ -@Deprecated -public abstract class AbstractEnumMarshaller> implements DynamoDbMarshaller { - - @Override - public String marshall(final T obj) { - try { - return obj.name(); - } catch (final RuntimeException e) { - throw failure(e, "Unable to marshall the instance of " + obj.getClass() + " into a string"); - } - } - - @Override - public T unmarshall(final Class clazz, final String obj) { - try { - return Enum.valueOf(clazz, obj); - } catch (final RuntimeException e) { - throw failure(e, "Unable to unmarshall the string " + obj + " into " + clazz); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ArgumentMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ArgumentMarshaller.java deleted file mode 100644 index 250b043ce8ac..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ArgumentMarshaller.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * Interface to make it possible to cache the expensive type determination - * behavior. - */ -public interface ArgumentMarshaller { - - /** - * Marshalls the object given into an AttributeValue. - */ - AttributeValue marshall(Object obj); - - interface BooleanAttributeMarshaller extends ArgumentMarshaller { - } - - interface StringAttributeMarshaller extends ArgumentMarshaller { - } - - interface NumberAttributeMarshaller extends ArgumentMarshaller { - } - - interface BinaryAttributeMarshaller extends ArgumentMarshaller { - } - - interface StringSetAttributeMarshaller extends ArgumentMarshaller { - } - - interface NumberSetAttributeMarshaller extends ArgumentMarshaller { - } - - interface BinarySetAttributeMarshaller extends ArgumentMarshaller { - } - - interface ListAttributeMarshaller extends ArgumentMarshaller { - } - - interface MapAttributeMarshaller extends ArgumentMarshaller { - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ArgumentUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ArgumentUnmarshaller.java deleted file mode 100644 index 85f74ed3c169..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ArgumentUnmarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.text.ParseException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * Unmarshaller interface to make it possible to cache the expensive - * type-determination behavior necessary when turning a service result back - * into an object. - */ -public interface ArgumentUnmarshaller { - - /** - * Asserts that the value given can be processed using the setter given. - */ - void typeCheck(AttributeValue value, Method setter); - - /** - * Unmarshalls the {@link AttributeValue} given into an instance of the - * appropriate type, as determined by {@link DynamoDbMapper} - * - * @throws ParseException when unable to parse a date string - */ - Object unmarshall(AttributeValue value) throws ParseException; -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformer.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformer.java deleted file mode 100644 index 0a97c4ed7b20..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformer.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A hook allowing a custom transform/untransform of the raw attribute - * values immediately before writing them into DynamoDB and immediately - * after reading them out of DynamoDB, but with extra context about - * the model class not available at the raw DynamoDbClient level. - *

    - * This interface contains both a {@code transform} method and a corresponding - * {@code untransform} method. These methods SHOULD be inverses, such that - * untransform(transform(value)) == value. - */ -public interface AttributeTransformer { - /** - * Transforms the input set of attribute values derived from the model - * object before writing them into DynamoDB. - * - * @param parameters transformation parameters - * @return the transformed attribute value map - */ - Map transform(Parameters parameters); - - /** - * Untransform the input set of attribute values read from DynamoDB before - * creating a model object from them. - * - * @param parameters transformation parameters - * @return the untransformed attribute value map - */ - Map untransform(Parameters parameters); - - /** - * Parameters for the {@code transform} and {@code untransform} methods, - * so we don't have to break the interface in order to add additional - * parameters. - *

    - * Consuming code should NOT implement this interface. - */ - interface Parameters { - /** - * Returns the raw attribute values to be transformed or untransformed. - * The returned map is not modifiable. - * - * @return the raw attribute values to transform or untransform - */ - Map getAttributeValues(); - - /** - * Returns true if this transformation is being called as part of a - * partial update operation. If true, the attributes returned by - * {@link #getAttributeValues()} do not represent the entire new - * item, but only a snapshot of the attributes which are getting - * new values. - *

    - * Implementations which do not support transforming a partial - * view of an item (for example, because they need to calculate a - * signature based on all of the item's attributes that won't be valid - * if only a subset of the attributes are taken into consideration) - * should check this flag and throw an exception rather than than - * corrupting the data in DynamoDB. - *

    - * This method always returns {@code false} for instances passed to - * {@link AttributeTransformer#untransform(Parameters)}. - * - * @return true if this operation is a partial update, false otherwise - */ - boolean isPartialUpdate(); - - /** - * @return the type of the model class we're transforming to or from - */ - Class modelClass(); - - /** - * @return the mapper config for this operation - */ - DynamoDbMapperConfig mapperConfig(); - - /** - * @return the name of the DynamoDB table the attributes were read - * from or will be written to - */ - String getTableName(); - - /** - * @return the name of the hash key for the table - */ - String getHashKeyName(); - - /** - * @return the name of the range key for the table, if it has one, - * otherwise {@code null} - */ - String getRangeKeyName(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformerChain.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformerChain.java deleted file mode 100644 index e6ae9badba01..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformerChain.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A virtual {@code AttributeTransformer} that transforms and untransforms - * attributes by running them through a cascading series of child - * {@code AttributeTransformer} instances. - */ -public class AttributeTransformerChain implements AttributeTransformer { - - private final List transformers; - - /** - * Creates a new transformer chain from the given array of transformers. - * When transforming attributes, these transformers are invoked from first - * to last; when untransforming they are invoked in the opposite order. - * - * @param transformers the chain of transformers. - */ - public AttributeTransformerChain( - final AttributeTransformer... transformers) { - - this(Arrays.asList(transformers)); - } - - /** - * Creates a new transformer chain from the given list of transformers. - * When transforming attributes, these transformers are invoked from first - * to last; when untransforming they are invoked in the opposite order. - * - * @param transformers the chain of transformers. - */ - public AttributeTransformerChain( - final List transformers) { - - this.transformers = Collections.unmodifiableList( - new ArrayList(transformers)); - } - - /** - * @return the transformers in this chain - */ - public List getTransformers() { - return transformers; - } - - @Override - public Map transform( - final Parameters parameters) { - - ProxyParameters proxy = new ProxyParameters(parameters); - - for (int i = 0; i < transformers.size(); ++i) { - proxy.setAttributeValues(transformers.get(i).transform(proxy)); - } - - return proxy.getAttributeValues(); - } - - @Override - public Map untransform( - final Parameters parameters) { - - ProxyParameters proxy = new ProxyParameters(parameters); - - for (int i = transformers.size() - 1; i >= 0; --i) { - proxy.setAttributeValues(transformers.get(i).untransform(proxy)); - } - - return proxy.getAttributeValues(); - } - - @Override - public String toString() { - return transformers.toString(); - } - - /** - * A {@code Parameters} proxy that intercepts calls to - * {@code getAttributeValues} and overrides the return value. - */ - private static class ProxyParameters implements Parameters { - - private final Parameters delegate; - private Map values; - - /** - * Create a new proxy wrapping the given {@code Parameters} object. - * - * @param delegate the parameters object to wrap - */ - ProxyParameters(final Parameters delegate) { - this.delegate = delegate; - this.values = delegate.getAttributeValues(); - } - - @Override - public Map getAttributeValues() { - return values; - } - - /** - * Changes the attribute values for this instance. - * - * @param values the new values - */ - public void setAttributeValues( - final Map values) { - this.values = Collections.unmodifiableMap(values); - } - - @Override - public boolean isPartialUpdate() { - return delegate.isPartialUpdate(); - } - - @Override - public Class modelClass() { - return delegate.modelClass(); - } - - @Override - public DynamoDbMapperConfig mapperConfig() { - return delegate.mapperConfig(); - } - - @Override - public String getTableName() { - return delegate.getTableName(); - } - - @Override - public String getHashKeyName() { - return delegate.getHashKeyName(); - } - - @Override - public String getRangeKeyName() { - return delegate.getRangeKeyName(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformerChainTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformerChainTest.java deleted file mode 100644 index d5be9c5680bb..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/AttributeTransformerChainTest.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.AttributeTransformer.Parameters; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class AttributeTransformerChainTest { - @Test - public void testTransformEmptyChain() { - - AttributeTransformer transformer = new AttributeTransformerChain( - Collections.emptyList() - ); - - Map values = - new HashMap(); - - Parameters params = new TestParameters(values); - - Map result = transformer.transform(params); - Assert.assertSame(values, result); - Assert.assertTrue(values.isEmpty()); - } - - @Test - public void testUntransformEmptyChain() { - - AttributeTransformer transformer = new AttributeTransformerChain( - Collections.emptyList() - ); - - Map values = - new HashMap(); - - Parameters params = new TestParameters(values); - - Map result = transformer.untransform(params); - Assert.assertSame(values, result); - Assert.assertTrue(values.isEmpty()); - } - - @Test - public void testTransform() { - - AttributeTransformer transformer1 = new TestTransformer(".one"); - AttributeTransformer transformer2 = new TestTransformer(".two"); - - AttributeTransformer chain = - new AttributeTransformerChain(transformer1, transformer2); - - Map values = - new HashMap(); - - values.put("test1", AttributeValue.builder().s("foo").build()); - values.put("test2", AttributeValue.builder().s("bar").build()); - - Parameters params = new TestParameters(values); - - Map result = chain.transform(params); - - Assert.assertNotNull(result); - Assert.assertEquals(2, result.size()); - - Assert.assertEquals("foo.one.two", result.get("test1").s()); - Assert.assertEquals("bar.one.two", result.get("test2").s()); - } - - @Test - public void testUntransform() { - - AttributeTransformer transformer1 = new TestTransformer(".one"); - AttributeTransformer transformer2 = new TestTransformer(".two"); - - AttributeTransformer chain = - new AttributeTransformerChain(transformer1, transformer2); - - Map values = - new HashMap(); - - values.put("test1", AttributeValue.builder().s("foo.one.two").build()); - values.put("test2", AttributeValue.builder().s("bar.one.two").build()); - - Parameters params = new TestParameters(values); - - Map result = chain.untransform(params); - - Assert.assertNotNull(result); - Assert.assertEquals(2, result.size()); - - Assert.assertEquals("foo", result.get("test1").s()); - Assert.assertEquals("bar", result.get("test2").s()); - } - - @Test - public void testRoundTrip() { - - AttributeTransformer transformer1 = new TestTransformer(".one"); - AttributeTransformer transformer2 = new TestTransformer(".two"); - - AttributeTransformer chain = - new AttributeTransformerChain(transformer1, transformer2); - - Map values = - new HashMap(); - - values.put("test1", AttributeValue.builder().s("foo").build()); - values.put("test2", AttributeValue.builder().s("bar").build()); - - Parameters params = new TestParameters(values); - - Map result = chain.transform(params); - - params = new TestParameters(result); - - result = chain.untransform(params); - - Assert.assertEquals(values, result); - } - - private static class TestTransformer implements AttributeTransformer { - - private final String appendMe; - - public TestTransformer(final String appendMe) { - this.appendMe = appendMe; - } - - @Override - public Map transform( - final Parameters parameters) { - - Map rval = - new HashMap(); - - for (Map.Entry entry - : parameters.getAttributeValues().entrySet()) { - - rval.put(entry.getKey(), transform(entry.getValue())); - } - - return rval; - } - - @Override - public Map untransform( - final Parameters parameters) { - - Map rval = - new HashMap(); - - for (Map.Entry entry - : parameters.getAttributeValues().entrySet()) { - - rval.put(entry.getKey(), untransform(entry.getValue())); - } - - return rval; - } - - private AttributeValue transform(AttributeValue value) { - return AttributeValue.builder().s(value.s() + appendMe).build(); - } - - private AttributeValue untransform(AttributeValue value) { - String s = value.s(); - if (s.endsWith(appendMe)) { - return AttributeValue.builder().s( - s.substring(0, s.length() - appendMe.length())).build(); - } else { - return value; - } - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadContext.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadContext.java deleted file mode 100644 index 8a9375d401a3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadContext.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; -import software.amazon.awssdk.utils.Validate; - - -/** - * Container object that has information about the batch load request made to DynamoDB. - * - * @author avinam - */ -public class BatchLoadContext { - /** - * The BatchGetItemRequest. - */ - private BatchGetItemRequest batchGetItemRequest; - /** - * The BatchGetItemResponse returned by the DynamoDB client. - */ - private BatchGetItemResponse batchGetItemResponse; - /** - * The number of times the request has been retried. - */ - private int retriesAttempted; - - /** - * Instantiates a new BatchLoadContext. - * @param batchGetItemRequest see {@link BatchGetItemRequest}. - * */ - public BatchLoadContext(BatchGetItemRequest batchGetItemRequest) { - this.batchGetItemRequest = Validate.paramNotNull(batchGetItemRequest, "batchGetItemRequest"); - this.batchGetItemResponse = null; - this.retriesAttempted = 0; - } - - public BatchGetItemRequest getBatchGetItemRequest() { - return batchGetItemRequest; - } - - public void setBatchGetItemRequest(BatchGetItemRequest batchGetItemRequest) { - this.batchGetItemRequest = batchGetItemRequest; - } - - /** - * @return the BatchGetItemResponse - */ - public BatchGetItemResponse batchGetItemResponse() { - return batchGetItemResponse; - } - - /** - * @return the BatchGetItemResponse - */ - public void setBatchGetItemResponse(BatchGetItemResponse batchGetItemResponse) { - this.batchGetItemResponse = batchGetItemResponse; - } - - - /** - * @return the BatchGetItemRequest. - */ - public BatchGetItemRequest batchGetItemRequest() { - return batchGetItemRequest; - } - - /** - * Gets the retriesAttempted. - * - * @return the retriesAttempted - */ - public int getRetriesAttempted() { - return retriesAttempted; - } - - /** - * Sets retriesAttempted. - * - * @param retriesAttempted the number of retries attempted - */ - public void setRetriesAttempted(int retriesAttempted) { - this.retriesAttempted = retriesAttempted; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadRetryStrategyTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadRetryStrategyTest.java deleted file mode 100644 index 43f9aab165f0..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchLoadRetryStrategyTest.java +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; - -// Commenting out the test class as its broken and we don't support mapper yet -@RunWith(MockitoJUnitRunner.class) -public class BatchLoadRetryStrategyTest { - - @Test - public void dummyTest() { - - } - -// private static final String TABLE_NAME = "tableName"; -// private static final String TABLE_NAME2 = "tableName2"; -// private static final String TABLE_NAME3 = "tableName3"; -// private static final String HASH_ATTR = "hash"; -// -// // private static BatchGetItemResponse batchGetItemResponse; -// private static List itemsToGet; -// -// static { -// -// itemsToGet = new ArrayList(); -// itemsToGet.add(new Item3("Bruce Wayne")); -// itemsToGet.add(new Item2("Is")); -// itemsToGet.add(new Item("Batman")); -// } -// -// @Rule -// public final ExpectedException thrown = ExpectedException.none(); -// -// @Mock -// private DynamoDbClient ddbMock; -// @Mock -// private BatchGetItemRequest mockItemRequest; -// @Mock -// private BatchGetItemResponse mockItemResult; -// -// @Test -// public void testBatchReadCallFailure_NoRetry() { -// when(ddbMock.batchGetItem(any(DynamoDbRequest.class))) -// .thenReturn(buildDefaultGetItemResponse().toBuilder().unprocessedKeys(buildUnprocessedKeysMap(1)).build()); -// DynamoDbMapperConfig config = -// getConfigWithCustomBatchLoadRetryStrategy(new DynamoDbMapperConfig.NoRetryBatchLoadRetryStrategy()); -// DynamoDbMapper mapper = new DynamoDbMapper(ddbMock, config); -// -// thrown.expect(BatchGetItemException.class); -// mapper.batchLoad(itemsToGet); -// verify(ddbMock, times(1)).batchGetItem(any(BatchGetItemRequest.class)); -// } -// -// @Test -// public void testBatchReadCallFailure_Retry() { -// when(ddbMock.batchGetItem(any(BatchGetItemRequest.class))) -// .thenReturn(buildDefaultGetItemResponse().toBuilder().unprocessedKeys(buildUnprocessedKeysMap(1)).build()); -// -// DynamoDbMapper mapper = new DynamoDbMapper(ddbMock, getConfigWithCustomBatchLoadRetryStrategy(new BatchLoadRetryStrategyWithNoDelay(3))); -// -// -// thrown.expect(BatchGetItemException.class); -// mapper.batchLoad(itemsToGet); -// verify(ddbMock, times(4)).batchGetItem(any(BatchGetItemRequest.class)); -// } -// -// @Test -// public void testBatchReadCallSuccess_Retry() { -// when(ddbMock.batchGetItem(any(BatchGetItemRequest.class))) -// .thenReturn(buildDefaultGetItemResponse().toBuilder().unprocessedKeys(new HashMap<>(1)).build()); -// -// DynamoDbMapperConfig config = -// getConfigWithCustomBatchLoadRetryStrategy(new DynamoDbMapperConfig.DefaultBatchLoadRetryStrategy()); -// DynamoDbMapper mapper = new DynamoDbMapper(ddbMock, config); -// -// mapper.batchLoad(itemsToGet); -// verify(ddbMock, times(1)).batchGetItem(any(BatchGetItemRequest.class)); -// } -// -// @Test -// public void testBatchReadCallFailure_Retry_RetryOnCompleteFailure() { -// when(ddbMock.batchGetItem(any(BatchGetItemRequest.class))) -// .thenReturn(buildDefaultGetItemResponse().toBuilder().unprocessedKeys(buildUnprocessedKeysMap(3)).build()); -// DynamoDbMapperConfig config = -// getConfigWithCustomBatchLoadRetryStrategy(new DynamoDbMapperConfig.DefaultBatchLoadRetryStrategy()); -// DynamoDbMapper mapper = new DynamoDbMapper(ddbMock, config); -// -// thrown.expect(BatchGetItemException.class); -// mapper.batchLoad(itemsToGet); -// verify(ddbMock, times(6)).batchGetItem(any(BatchGetItemRequest.class)); -// } -// -// @Test -// public void testBatchReadCallFailure_NoRetry_RetryOnCompleteFailure() { -// when(ddbMock.batchGetItem(any(BatchGetItemRequest.class))) -// .thenReturn(buildDefaultGetItemResponse().toBuilder().unprocessedKeys(buildUnprocessedKeysMap(3)).build()); -// DynamoDbMapperConfig config = -// getConfigWithCustomBatchLoadRetryStrategy(new DynamoDbMapperConfig.NoRetryBatchLoadRetryStrategy()); -// DynamoDbMapper mapper = new DynamoDbMapper(ddbMock, config); -// -// thrown.expect(BatchGetItemException.class); -// mapper.batchLoad(itemsToGet); -// verify(ddbMock, times(1)).batchGetItem(any(BatchGetItemRequest.class)); -// } -// -// @Test -// public void testNoDelayOnPartialFailure_DefaultRetry() { -// BatchLoadRetryStrategy defaultRetryStrategy = new DynamoDbMapperConfig.DefaultBatchLoadRetryStrategy(); -// when(mockItemResult.unprocessedKeys()).thenReturn(buildUnprocessedKeysMap(2)); -// when(mockItemRequest.requestItems()).thenReturn(buildUnprocessedKeysMap(3)); -// BatchLoadContext context = new BatchLoadContext(mockItemRequest); -// context.setBatchGetItemResponse(mockItemResult); -// context.setRetriesAttempted(2); -// assertEquals(0, defaultRetryStrategy.getDelayBeforeNextRetry(context)); -// } -// -// @Test -// public void testDelayOnPartialFailure_DefaultRetry() { -// BatchLoadRetryStrategy defaultRetryStrategy = new DynamoDbMapperConfig.DefaultBatchLoadRetryStrategy(); -// when(mockItemResult.unprocessedKeys()).thenReturn(buildUnprocessedKeysMap(3)); -// when(mockItemRequest.requestItems()).thenReturn(buildUnprocessedKeysMap(3)); -// -// BatchLoadContext context = new BatchLoadContext(mockItemRequest); -// context.setBatchGetItemResponse(mockItemResult); -// context.setRetriesAttempted(2); -// assertTrue(defaultRetryStrategy.getDelayBeforeNextRetry(context) > 0); -// } -// -// private DynamoDbMapperConfig getConfigWithCustomBatchLoadRetryStrategy(final BatchLoadRetryStrategy batchReadRetryStrategy) { -// return new DynamoDbMapperConfig.Builder().withBatchLoadRetryStrategy(batchReadRetryStrategy).build(); -// } -// -// private Map buildUnprocessedKeysMap(final int size) { -// final Map unproccessedKeys = new HashMap(size); -// for (int i = 0; i < size; i++) { -// unproccessedKeys.put("test" + i, KeysAndAttributes.builder().build()); -// } -// -// return unproccessedKeys; -// } -// -// private BatchGetItemResponse buildDefaultGetItemResponse() { -// -// final Map>> map = new HashMap>>(); -// return BatchGetItemResponse.builder().responses(map).build(); -// -// } -// -// static class BatchLoadRetryStrategyWithNoDelay implements BatchLoadRetryStrategy { -// -// private final int maxRetry; -// -// public BatchLoadRetryStrategyWithNoDelay(final int maxRetry) { -// this.maxRetry = maxRetry; -// } -// -// /** -// * @see BatchLoadRetryStrategy#maxRetryOnUnprocessedKeys(java.util.Map, java.util.Map) -// */ -// @Override -// public boolean shouldRetry(final BatchLoadContext batchLoadContext) { -// return batchLoadContext.getRetriesAttempted() < maxRetry; -// } -// -// /** -// * @see BatchLoadRetryStrategy#getDelayBeforeNextRetry(java.util.Map, int) -// */ -// @Override -// public long getDelayBeforeNextRetry(final BatchLoadContext batchLoadContext) { -// return 0; -// } -// -// -// } -// -// @DynamoDbTable(tableName = TABLE_NAME) -// public static class Item { -// -// private String hash; -// -// public Item(final String hash) { -// this.hash = hash; -// } -// -// @DynamoDbAttribute(attributeName = HASH_ATTR) -// @DynamoDbHashKey -// public String getHash() { -// return hash; -// } -// -// public void setHash(final String hash) { -// this.hash = hash; -// } -// -// public WriteRequest toPutSaveRequest() { -// return WriteRequest.builder() -// .putRequest(PutRequest.builder() -// .item(Collections.singletonMap(HASH_ATTR, AttributeValue.builder().s(hash).build())) -// .build()) -// .build(); -// } -// } -// -// @DynamoDbTable(tableName = TABLE_NAME2) -// public static class Item2 { -// -// private String hash; -// -// public Item2(final String hash) { -// this.hash = hash; -// } -// -// @DynamoDbAttribute(attributeName = HASH_ATTR) -// @DynamoDbHashKey -// public String getHash() { -// return hash; -// } -// -// public void setHash(final String hash) { -// this.hash = hash; -// } -// -// public WriteRequest toPutSaveRequest() { -// return WriteRequest.builder() -// .putRequest(PutRequest.builder() -// .item(Collections.singletonMap(HASH_ATTR, AttributeValue.builder().s(hash) -// .build())) -// .build()) -// .build(); -// } -// } -// -// @DynamoDbTable(tableName = TABLE_NAME3) -// public static class Item3 { -// -// private String hash; -// -// public Item3(final String hash) { -// this.hash = hash; -// } -// -// @DynamoDbAttribute(attributeName = HASH_ATTR) -// @DynamoDbHashKey -// public String getHash() { -// return hash; -// } -// -// public void setHash(final String hash) { -// this.hash = hash; -// } -// -// public WriteRequest toPutSaveRequest() { -// return WriteRequest.builder() -// .putRequest(PutRequest.builder() -// .item(Collections.singletonMap(HASH_ATTR, AttributeValue.builder() -// .s(hash) -// .build())) -// .build()) -// .build(); -// } -// } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchWriteRetryStrategyTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchWriteRetryStrategyTest.java deleted file mode 100644 index 5d69001b2412..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/BatchWriteRetryStrategyTest.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import junit.framework.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper.FailedBatch; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.BatchWriteRetryStrategy; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemResponse; -import software.amazon.awssdk.services.dynamodb.model.PutRequest; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -@RunWith(MockitoJUnitRunner.class) -public class BatchWriteRetryStrategyTest { - - private static final int MAX_RETRY = 10; - private static final String TABLE_NAME = "tableName"; - private static final String HASH_ATTR = "hash"; - - private static Map> unprocessedItems; - - static { - WriteRequest writeReq = WriteRequest.builder() - .putRequest(PutRequest.builder() - .item(Collections.singletonMap( - HASH_ATTR, - AttributeValue.builder().s("foo").build())) - .build()) - .build(); - - unprocessedItems = Collections.singletonMap(TABLE_NAME, - Arrays.asList(writeReq)); - } - - @Mock - private DynamoDbClient ddbMock; - - private DynamoDbMapper mapper; - - @Before - public void setup() { - mapper = new DynamoDbMapper( - ddbMock, - getConfigWithCustomBatchWriteRetryStrategy( - new BatchWriteRetryStrategyWithNoDelay(MAX_RETRY))); - } - - @Test - public void testBatchWriteItemCallSuccess_NoRetry() { - when(ddbMock.batchWriteItem(any(BatchWriteItemRequest.class))) - .thenReturn(BatchWriteItemResponse.builder().unprocessedItems(Collections.>emptyMap()).build()); - - List failedBatches = mapper.batchSave(new Item("foo")); - - verify(ddbMock, times(1)).batchWriteItem(any(BatchWriteItemRequest.class)); - Assert.assertEquals(0, failedBatches.size()); - } - - @Test - public void testUnprocessedItemReturned_BatchWriteItemCallNotExceedMaxRetry() { - when(ddbMock.batchWriteItem(any(BatchWriteItemRequest.class))) - .thenReturn(BatchWriteItemResponse.builder().unprocessedItems(unprocessedItems).build()); - - List failedBatches = mapper.batchSave(new Item("foo")); - verify(ddbMock, times(MAX_RETRY + 1)).batchWriteItem(any(BatchWriteItemRequest.class)); - - Assert.assertEquals(1, failedBatches.size()); - FailedBatch failedBatch = failedBatches.get(0); - - Assert.assertEquals( - "Failed batch should contain the same UnprocessedItems returned in the BatchWriteItem response.", - unprocessedItems, - failedBatch.getUnprocessedItems()); - Assert.assertNull( - "No exception should be set if the batch failed after max retry", - failedBatch.getException()); - } - - @Test - public void testExceptionThrown_NoRetry() { - - RuntimeException exception = new RuntimeException("BOOM"); - - when(ddbMock.batchWriteItem(any(BatchWriteItemRequest.class))).thenThrow(exception); - - // put a random item - Item item = new Item(UUID.randomUUID().toString()); - List failedBatches = mapper.batchSave(item); - - Assert.assertEquals(1, failedBatches.size()); - FailedBatch failedBatch = failedBatches.get(0); - - Assert.assertEquals( - "Failed batch should contain all the input items for batchWrite", - Collections.singletonMap(TABLE_NAME, Arrays.asList(item.toPutSaveRequest())), - failedBatch.getUnprocessedItems()); - Assert.assertSame( - "The exception should be the same as one thrown by BatchWriteItem", - exception, - failedBatch.getException()); - } - - private DynamoDbMapperConfig getConfigWithCustomBatchWriteRetryStrategy( - BatchWriteRetryStrategy batchWriteRetryStrategy) { - return new DynamoDbMapperConfig.Builder() - .withBatchWriteRetryStrategy(batchWriteRetryStrategy) - .build(); - } - - private static class BatchWriteRetryStrategyWithNoDelay implements - BatchWriteRetryStrategy { - - private final int maxRetry; - - public BatchWriteRetryStrategyWithNoDelay(int maxRety) { - this.maxRetry = maxRety; - } - - @Override - public int maxRetryOnUnprocessedItems( - Map> batchWriteItemInput) { - return maxRetry; - } - - @Override - public long getDelayBeforeRetryUnprocessedItems( - Map> unprocessedItems, - int retriesAttempted) { - return 0; - } - - } - - @DynamoDbTable(tableName = TABLE_NAME) - public static class Item { - - private String hash; - - public Item(String hash) { - this.hash = hash; - } - - @DynamoDbHashKey - @DynamoDbAttribute(attributeName = HASH_ATTR) - public String getHash() { - return hash; - } - - public void setHash(String hash) { - this.hash = hash; - } - - public WriteRequest toPutSaveRequest() { - return WriteRequest.builder() - .putRequest(PutRequest.builder() - .item(Collections.singletonMap(HASH_ATTR, AttributeValue.builder().s(hash).build())) - .build()) - .build(); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/CachingMarshallerSetTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/CachingMarshallerSetTest.java deleted file mode 100644 index 621d59254c8b..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/CachingMarshallerSetTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayDeque; -import java.util.Deque; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchemas.CachingMarshallerSet; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchemas.MarshallerSet; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.TestClass; - -public class CachingMarshallerSetTest { - - private static final TestMarshallerSet MOCK = new TestMarshallerSet(); - private static final MarshallerSet SUT = new CachingMarshallerSet(MOCK); - - @Test - public void testIt() throws Exception { - ArgumentMarshaller marshaller = new ArgumentMarshaller() { - @Override - public AttributeValue marshall(Object value) { - return null; - } - }; - - MOCK.queue.add(marshaller); - - ArgumentMarshaller result = SUT.marshaller( - TestClass.class.getMethod("getString")); - - Assert.assertSame(marshaller, result); - - result = SUT.marshaller(TestClass.class.getMethod("getString")); - - Assert.assertSame(marshaller, result); - - ArgumentMarshaller marshaller2 = new ArgumentMarshaller() { - @Override - public AttributeValue marshall(Object value) { - return null; - } - }; - - MOCK.queue.add(marshaller2); - - result = SUT.marshaller(TestClass.class.getMethod("getInt")); - - Assert.assertSame(marshaller2, result); - } - - private static class TestMarshallerSet implements MarshallerSet { - - private final Deque queue = - new ArrayDeque(); - - private final Deque memberQueue = - new ArrayDeque(); - - @Override - public ArgumentMarshaller marshaller(Method getter) { - return queue.remove(); - } - - @Override - public ArgumentMarshaller memberMarshaller(Type memberType) { - return memberQueue.remove(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/CachingUnmarshallerSetTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/CachingUnmarshallerSetTest.java deleted file mode 100644 index cb1afce82b2e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/CachingUnmarshallerSetTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.lang.reflect.Type; -import java.util.ArrayDeque; -import java.util.Deque; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchemas.CachingUnmarshallerSet; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchemas.UnmarshallerSet; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.TestClass; - -public class CachingUnmarshallerSetTest { - - private static final TestUnmarshallerSet MOCK = new TestUnmarshallerSet(); - private static final UnmarshallerSet SUT = new CachingUnmarshallerSet(MOCK); - - @Test - public void testIt() throws Exception { - ArgumentUnmarshaller unmarshaller = new ArgumentUnmarshaller() { - @Override - public void typeCheck(AttributeValue value, Method setter) { - } - - @Override - public Object unmarshall(AttributeValue value) { - return null; - } - }; - - MOCK.queue.add(unmarshaller); - - ArgumentUnmarshaller result = SUT.getUnmarshaller( - TestClass.class.getMethod("getString"), - TestClass.class.getMethod("setString", String.class)); - - Assert.assertSame(unmarshaller, result); - - result = SUT.getUnmarshaller( - TestClass.class.getMethod("getString"), - TestClass.class.getMethod("setString", String.class)); - - Assert.assertSame(unmarshaller, result); - - ArgumentUnmarshaller unmarshaller2 = new ArgumentUnmarshaller() { - @Override - public void typeCheck(AttributeValue value, Method setter) { - } - - @Override - public Object unmarshall(AttributeValue value) { - return null; - } - }; - - MOCK.queue.add(unmarshaller2); - - result = SUT.getUnmarshaller( - TestClass.class.getMethod("getInt"), - TestClass.class.getMethod("setInt", int.class)); - - Assert.assertSame(unmarshaller2, result); - } - - private static class TestUnmarshallerSet implements UnmarshallerSet { - - private final Deque queue = - new ArrayDeque(); - - private final Deque memberQueue = - new ArrayDeque(); - - @Override - public ArgumentUnmarshaller getUnmarshaller(Method getter, Method setter) { - return queue.remove(); - } - - @Override - public ArgumentUnmarshaller memberUnmarshaller(Type type) { - return memberQueue.remove(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConfigureS3LinksTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConfigureS3LinksTest.java deleted file mode 100644 index 34e9f14eb9ef..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConfigureS3LinksTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; - -import org.junit.Before; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.regions.Region; - -public class ConfigureS3LinksTest { - - private S3ClientCache s3cc; - - @Before - public void setUp() throws Exception { - s3cc = new S3ClientCache(AwsBasicCredentials.create("mock", "mock")); - } - - @Test - public void testS3LinkWithStringRegion() { - CorrectTestClass obj = new CorrectTestClass(); - S3Link s3 = new S3Link(s3cc, "ap-southeast-1", "nonexisting-test-bucketname2", "key"); - obj.setS3(s3); - - assertNotNull(obj.s3()); - assertEquals("nonexisting-test-bucketname2", obj.s3().bucketName()); - assertSame(Region.AP_SOUTHEAST_1.id(), obj.s3().s3Region().id()); - assertSame("ap-southeast-1", obj.s3().getRegion()); - } - - @Test - public void testManyS3LinksClass() { - ManyS3LinksTestClass obj = new ManyS3LinksTestClass(); - assertNull(obj.s31()); - } - - @DynamoDbTable(tableName = "nonexisting-test-tablename") - public static class CorrectTestClass { - - private String hk; - private S3Link s3; - - public CorrectTestClass() { - } - - @DynamoDbHashKey - public String getHk() { - return hk; - } - - public void setHk(String hk) { - this.hk = hk; - } - - public S3Link s3() { - return s3; - } - - public void setS3(S3Link s3) { - this.s3 = s3; - } - } - - @DynamoDbTable(tableName = "nonexisting-test-tablename") - public static class ManyS3LinksTestClass { - - private String hk; - private S3Link s31; - private S3Link s32; - private S3Link s33; - private S3Link s34; - private S3Link s35; - private S3Link s36; - - public ManyS3LinksTestClass() { - } - - @DynamoDbHashKey - public String getHk() { - return hk; - } - - public void setHk(String hk) { - this.hk = hk; - } - - public S3Link s31() { - return s31; - } - - public void setS31(S3Link s31) { - this.s31 = s31; - } - - public S3Link s32() { - return s32; - } - - public void setS32(S3Link s32) { - this.s32 = s32; - } - - public S3Link s33() { - return s33; - } - - public void setS33(S3Link s33) { - this.s33 = s33; - } - - public S3Link s34() { - return s34; - } - - public void setS34(S3Link s34) { - this.s34 = s34; - } - - public S3Link s35() { - return s35; - } - - public void setS35(S3Link s35) { - this.s35 = s35; - } - - public S3Link s36() { - return s36; - } - - public void setS36(S3Link s36) { - this.s36 = s36; - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionSchema.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionSchema.java deleted file mode 100644 index 5aa31c40fbeb..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionSchema.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.HashMap; -import java.util.Map; - -/** - * A strategy for mapping between Java types and DynamoDB types. Serves as a - * factory for {@code ItemConverter} instances that implement this mapping. - * Standard implementations are available in the {@link ConversionSchemas} - * class. - */ -public interface ConversionSchema { - - /** - * Creates an {@code ItemConverter}, injecting dependencies from the - * {@code DynamoDBMapper} that needs it. - * - * @param dependencies the dependencies to inject - * @return a new ItemConverter - */ - ItemConverter getConverter(Dependencies dependencies); - - /** - * Dependency injection for the {@code ItemConverter}s that this - * {@code ConversionSchema} generates. - */ - class Dependencies { - - private final Map, Object> values; - - public Dependencies() { - values = new HashMap, Object>(); - } - - @SuppressWarnings("unchecked") - public T get(Class clazz) { - return (T) values.get(clazz); - } - - public Dependencies with(Class clazz, T value) { - values.put(clazz, value); - return this; - } - - @Override - public String toString() { - return values.toString(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionSchemas.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionSchemas.java deleted file mode 100644 index 4249ecce85e6..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionSchemas.java +++ /dev/null @@ -1,1493 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BinaryAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BinarySetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BooleanAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.ListAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.MapAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.NumberAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.NumberSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardBeanProperties.Bean; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardModelFactories.Rule; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardModelFactories.RuleFactory; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.BooleanSetToNumberSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.BooleanToBooleanMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.BooleanToNumberMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ByteArraySetToBinarySetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ByteArrayToBinaryMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ByteBufferSetToBinarySetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ByteBufferToBinaryMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.CalendarSetToStringSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.CalendarToStringMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.CollectionToListMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.CustomMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.DateSetToStringSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.DateToStringMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.MapToMapMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.NumberSetToNumberSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.NumberToNumberMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ObjectSetToStringSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ObjectToMapMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.ObjectToStringMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.S3LinkToStringMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.StringSetToStringSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.StringToStringMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.marshallers.UuidSetToStringSetMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.BigDecimalSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.BigDecimalUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.BigIntegerSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.BigIntegerUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.BooleanSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.BooleanUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ByteArraySetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ByteArrayUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ByteBufferSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ByteBufferUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ByteSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ByteUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.CalendarSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.CalendarUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.CustomUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.DateSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.DateUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.DoubleSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.DoubleUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.FloatSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.FloatUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.IntegerSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.IntegerUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ListUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.LongSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.LongUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.MapUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.NullableUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ObjectSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ObjectUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.S3LinkUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ShortSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.ShortUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.StringSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.StringUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.UuidSetUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers.UuidUnmarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * Pre-defined strategies for mapping between Java types and DynamoDB types. - */ -public final class ConversionSchemas { - - /** - * The V1 schema mapping, which retains strict backwards compatibility with - * the original DynamoDB data model. In particular, it marshals Java - * Booleans as DynamoDB Numbers rather than the newer Boolean type, and does - * not support marshaling Lists or Maps. It can unmarshal - * values written in newer formats to ease migration. - *

    - * Use me if you have other code still using an old version of the SDK that - * does not understand the new List and Map types and want to ensure that - * you don't accidentally start writing values using these types. - */ - public static final ConversionSchema V1 = v1Builder("V1ConversionSchema").build(); - /** - * A V2 conversion schema which retains backwards compatibility with the - * V1 conversion schema for existing DynamoDB types, but adds the ability - * to marshall recursive structures using the new List and Map types. This - * is currently the default conversion schema. - */ - public static final ConversionSchema V2_COMPATIBLE = v2CompatibleBuilder( - "V2CompatibleConversionSchema").build(); - /** - * The native V2 conversion schema. This schema breaks compatibility with - * older versions of the mapper that only support the V1 schema by - * storing booleans as native DynamoDB Booleans rather than as a 1 or 0 - * in a DynamoDB Number. Switching to the V2 schema will prevent older - * versions of the mapper from reading items you write that contain - * booleans. - */ - public static final ConversionSchema V2 = v2Builder("V2ConversionSchema").build(); - static final ConversionSchema DEFAULT = V2_COMPATIBLE; - private static final Logger log = - LoggerFactory.getLogger(ConversionSchemas.class); - - ConversionSchemas() { - throw new UnsupportedOperationException(); - } - - /** - * A ConversionSchema builder that defaults to building {@link #V1}. - */ - public static Builder v1Builder(String name) { - return new Builder(name, V1MarshallerSet.marshallers(), V1MarshallerSet.setMarshallers(), - StandardUnmarshallerSet.unmarshallers(), - StandardUnmarshallerSet.setUnmarshallers()); - } - - /** - * A ConversionSchema builder that defaults to building {@link #V2_COMPATIBLE}. - */ - public static Builder v2CompatibleBuilder(String name) { - return new Builder(name, V2CompatibleMarshallerSet.marshallers(), - V2CompatibleMarshallerSet.setMarshallers(), - StandardUnmarshallerSet.unmarshallers(), - StandardUnmarshallerSet.setUnmarshallers()); - } - - /** - * A ConversionSchema builder that defaults to building {@link #V2}. - */ - public static Builder v2Builder(String name) { - return new Builder(name, V2MarshallerSet.marshallers(), V2MarshallerSet.setMarshallers(), - StandardUnmarshallerSet.unmarshallers(), - StandardUnmarshallerSet.setUnmarshallers()); - } - - private static void addStandardDateMarshallers( - List> list) { - - list.add(Pair.of(Date.class, - DateToStringMarshaller.instance())); - list.add(Pair.of(Calendar.class, - CalendarToStringMarshaller.instance())); - } - - private static void addV1BooleanMarshallers( - List> list) { - - list.add(Pair.of(Boolean.class, - BooleanToNumberMarshaller.instance())); - list.add(Pair.of(boolean.class, - BooleanToNumberMarshaller.instance())); - } - - private static void addV2BooleanMarshallers( - List> list) { - - list.add(Pair.of(Boolean.class, - BooleanToBooleanMarshaller.instance())); - list.add(Pair.of(boolean.class, - BooleanToBooleanMarshaller.instance())); - } - - private static void addStandardNumberMarshallers( - List> list) { - - list.add(Pair.of(Number.class, - NumberToNumberMarshaller.instance())); - list.add(Pair.of(byte.class, - NumberToNumberMarshaller.instance())); - list.add(Pair.of(short.class, - NumberToNumberMarshaller.instance())); - list.add(Pair.of(int.class, - NumberToNumberMarshaller.instance())); - list.add(Pair.of(long.class, - NumberToNumberMarshaller.instance())); - list.add(Pair.of(float.class, - NumberToNumberMarshaller.instance())); - list.add(Pair.of(double.class, - NumberToNumberMarshaller.instance())); - } - - private static void addStandardStringMarshallers( - List> list) { - - list.add(Pair.of(String.class, - StringToStringMarshaller.instance())); - - list.add(Pair.of(UUID.class, - ObjectToStringMarshaller.instance())); - } - - private static void addStandardBinaryMarshallers( - List> list) { - - list.add(Pair.of(ByteBuffer.class, - ByteBufferToBinaryMarshaller.instance())); - list.add(Pair.of(byte[].class, - ByteArrayToBinaryMarshaller.instance())); - } - - private static void addStandardS3LinkMarshallers( - List> list) { - - list.add(Pair.of(S3Link.class, - S3LinkToStringMarshaller.instance())); - } - - private static void addStandardDateSetMarshallers( - List> list) { - - list.add(Pair.of(Date.class, - DateSetToStringSetMarshaller.instance())); - list.add(Pair.of(Calendar.class, - CalendarSetToStringSetMarshaller.instance())); - } - - private static void addStandardNumberSetMarshallers( - List> list) { - - list.add(Pair.of(Number.class, - NumberSetToNumberSetMarshaller.instance())); - list.add(Pair.of(byte.class, - NumberSetToNumberSetMarshaller.instance())); - list.add(Pair.of(short.class, - NumberSetToNumberSetMarshaller.instance())); - list.add(Pair.of(int.class, - NumberSetToNumberSetMarshaller.instance())); - list.add(Pair.of(long.class, - NumberSetToNumberSetMarshaller.instance())); - list.add(Pair.of(float.class, - NumberSetToNumberSetMarshaller.instance())); - list.add(Pair.of(double.class, - NumberSetToNumberSetMarshaller.instance())); - } - - private static void addStandardStringSetMarshallers( - List> list) { - - list.add(Pair.of(String.class, - StringSetToStringSetMarshaller.instance())); - - list.add(Pair.of(UUID.class, - UuidSetToStringSetMarshaller.instance())); - } - - private static void addStandardBinarySetMarshallers( - List> list) { - - list.add(Pair.of(ByteBuffer.class, - ByteBufferSetToBinarySetMarshaller.instance())); - list.add(Pair.of(byte[].class, - ByteArraySetToBinarySetMarshaller.instance())); - } - - private static void addV1BooleanSetMarshallers( - List> list) { - - list.add(Pair.of(Boolean.class, - BooleanSetToNumberSetMarshaller.instance())); - list.add(Pair.of(boolean.class, - BooleanSetToNumberSetMarshaller.instance())); - } - - private static Class unwrapGenericSetParam(Type setType) { - if (!(setType instanceof ParameterizedType)) { - log.warn("Set type {} is not a ParameterizedType, using default marshaller and unmarshaller", setType); - return Object.class; - } - - ParameterizedType ptype = (ParameterizedType) setType; - Type[] arguments = ptype.getActualTypeArguments(); - - if (arguments.length != 1) { - log.warn("Set type {} does not have exactly one type argument, using default marshaller and unmarshaller", setType); - return Object.class; - } - - if (arguments[0].toString().equals("byte[]")) { - return byte[].class; - } else { - return (Class) arguments[0]; - } - } - - private static Class resolveClass(Type type) { - Type localType = type; - if (localType instanceof ParameterizedType) { - localType = ((ParameterizedType) type).getRawType(); - } - if (!(localType instanceof Class)) { - throw new DynamoDbMappingException("Cannot resolve class for type " - + type); - } - return (Class) localType; - } - - private static T find(Class needle, List> haystack) { - for (Pair pair : haystack) { - if (pair.key.isAssignableFrom(needle)) { - return pair.value; - } - } - return null; - } - - interface MarshallerSet { - ArgumentMarshaller marshaller(Method getter); - - ArgumentMarshaller memberMarshaller(Type memberType); - } - - interface UnmarshallerSet { - ArgumentUnmarshaller getUnmarshaller(Method getter, Method setter); - - ArgumentUnmarshaller memberUnmarshaller(Type memberType); - } - - public static class Builder { - - private final String name; - private final List> marshallers; - private final List> setMarshallers; - private final List> unmarshallers; - private final List> setUnmarshallers; - - Builder(String name, List> marshallers, - List> setMarshallers, - List> unmarshallers, - List> setUnmarshallers) { - this.name = name; - this.marshallers = marshallers; - this.setMarshallers = setMarshallers; - this.unmarshallers = unmarshallers; - this.setUnmarshallers = setUnmarshallers; - } - - /** - * Adds marshaling of a type to the schema. Types are in LIFO order, so the last type added - * will be the first matched. - */ - public Builder addFirstType(Class clazz, ArgumentMarshaller marshaller, - ArgumentUnmarshaller unmarshaller) { - this.marshallers.add(0, Pair.of(clazz, marshaller)); - this.unmarshallers.add(0, Pair.of(clazz, unmarshaller)); - return this; - } - - /** - * Adds marshaling of a Set of a type to the schema. Types are in LIFO order, so the last - * type added will be the first matched. - */ - public Builder addFirstSetType(Class clazz, ArgumentMarshaller marshaller, - ArgumentUnmarshaller unmarshaller) { - this.setMarshallers.add(0, Pair.of(clazz, marshaller)); - this.setUnmarshallers.add(0, Pair.of(clazz, unmarshaller)); - return this; - } - - public ConversionSchema build() { - return new StandardConversionSchema(name, new AbstractMarshallerSet(marshallers, - setMarshallers), - new StandardUnmarshallerSet(unmarshallers, - setUnmarshallers)); - } - } - - static class StandardConversionSchema implements ConversionSchema { - - private final String name; - private final MarshallerSet marshallers; - private final UnmarshallerSet unmarshallers; - - StandardConversionSchema( - String name, - MarshallerSet marshallers, - UnmarshallerSet unmarshallers) { - - this.name = name; - this.marshallers = new CachingMarshallerSet( - new AnnotationAwareMarshallerSet(marshallers)); - - this.unmarshallers = new CachingUnmarshallerSet( - new AnnotationAwareUnmarshallerSet(unmarshallers)); - } - - @Override - public ItemConverter getConverter(Dependencies dependencies) { - - S3ClientCache s3cc = dependencies.get(S3ClientCache.class); - - return new StandardItemConverter( - marshallers, - unmarshallers, - s3cc); - } - - @Override - public String toString() { - return name; - } - } - - static class StandardItemConverter implements ItemConverter { - - private final MarshallerSet marshallerSet; - private final UnmarshallerSet unmarshallerSet; - private final S3ClientCache s3cc; - - StandardItemConverter( - MarshallerSet marshallerSet, - UnmarshallerSet unmarshallerSet, - S3ClientCache s3cc) { - - this.marshallerSet = marshallerSet; - this.unmarshallerSet = unmarshallerSet; - this.s3cc = s3cc; - } - - private static Object unmarshall( - ArgumentUnmarshaller unmarshaller, - Method setter, - AttributeValue value) { - - unmarshaller.typeCheck(value, setter); - - try { - - return unmarshaller.unmarshall(value); - - } catch (IllegalArgumentException e) { - throw new DynamoDbMappingException( - "Couldn't unmarshall value " + value + " for " + setter, - e); - - } catch (ParseException e) { - throw new DynamoDbMappingException( - "Error attempting to parse date string " + value + " for " - + setter, - e); - } - } - - private static T createObject(Class clazz) { - try { - - return clazz.newInstance(); - - } catch (InstantiationException e) { - throw new DynamoDbMappingException( - "Failed to instantiate new instance of class", e); - - } catch (IllegalAccessException e) { - throw new DynamoDbMappingException( - "Failed to instantiate new instance of class", e); - } - } - - @Override - public DynamoDbMapperFieldModel getFieldModel(Method getter) { - final ArgumentMarshaller marshaller = marshaller(getter); - - final DynamoDbAttributeType attributeType; - if (marshaller instanceof StringAttributeMarshaller) { - attributeType = DynamoDbAttributeType.S; - } else if (marshaller instanceof NumberAttributeMarshaller) { - attributeType = DynamoDbAttributeType.N; - } else if (marshaller instanceof BinaryAttributeMarshaller) { - attributeType = DynamoDbAttributeType.B; - } else if (marshaller instanceof StringSetAttributeMarshaller) { - attributeType = DynamoDbAttributeType.SS; - } else if (marshaller instanceof NumberSetAttributeMarshaller) { - attributeType = DynamoDbAttributeType.NS; - } else if (marshaller instanceof BinarySetAttributeMarshaller) { - attributeType = DynamoDbAttributeType.BS; - } else if (marshaller instanceof BooleanAttributeMarshaller) { - attributeType = DynamoDbAttributeType.BOOL; - } else if (marshaller instanceof ListAttributeMarshaller) { - attributeType = DynamoDbAttributeType.L; - } else if (marshaller instanceof MapAttributeMarshaller) { - attributeType = DynamoDbAttributeType.M; - } else { - throw new DynamoDbMappingException( - "Unrecognized marshaller type for " + getter + ": " - + marshaller); - } - - // Note, generating the attribute name using this method is not - // actually correct for @DynamoDBFlattened attributes, however, - // its the best that can be done given only the method. The - // proper way to get this information is using the model factory. - final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter, null); - final DynamoDbMapperFieldModel.Builder builder = new DynamoDbMapperFieldModel.Builder(void.class, annotations); - builder.with(attributeType); - return builder.build(); - } - - @Override - public AttributeValue convert(Method getter, Object object) { - if (object == null) { - return null; - } - - ArgumentMarshaller marshaller = marshaller(getter); - return marshaller.marshall(object); - } - - @Override - public Map convert(Object object) { - if (object == null) { - return null; - } - - Class clazz = (Class) object.getClass(); - Map result = - new HashMap(); - - for (final Bean bean : StandardBeanProperties.of(clazz).map().values()) { - Object getterResult = bean.reflect().get(object); - if (getterResult != null) { - AttributeValue value = convert(bean.type().getter(), getterResult); - if (value != null) { - result.put(bean.properties().attributeName(), value); - } - } - } - - return result; - } - - private ArgumentMarshaller marshaller(Method getter) { - ArgumentMarshaller marshaller = - marshallerSet.marshaller(getter); - - marshaller = augment(getter.getGenericReturnType(), marshaller); - - return marshaller; - } - - private ArgumentMarshaller memberMarshaller(Type type) { - ArgumentMarshaller marshaller = - marshallerSet.memberMarshaller(type); - - marshaller = augment(type, marshaller); - - return marshaller; - } - - private ArgumentMarshaller augment( - Type type, - ArgumentMarshaller marshaller) { - - if (marshaller instanceof CollectionToListMarshaller) { - return getCollectionToListMarshaller(type); - } - - if (marshaller instanceof MapToMapMarshaller) { - return mapToMapMarshaller(type); - } - if (marshaller instanceof ObjectToMapMarshaller) { - return getObjectToMapMarshaller(type); - } - - return marshaller; - } - - private ArgumentMarshaller getCollectionToListMarshaller(Type type) { - if (!(type instanceof ParameterizedType)) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the " - + "Collection type " + type + ", which is not " - + "parameterized."); - } - - ParameterizedType ptype = (ParameterizedType) type; - Type[] args = ptype.getActualTypeArguments(); - - if (args == null || args.length != 1) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the " - + "Collection type " + type + "; unexpected number of " - + "type arguments."); - } - - ArgumentMarshaller memberMarshaller = - memberMarshaller(args[0]); - - return new CollectionToListMarshaller(memberMarshaller); - } - - private ArgumentMarshaller mapToMapMarshaller(Type type) { - if (!(type instanceof ParameterizedType)) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the Map " - + "type " + type + ", which is not parameterized."); - } - - ParameterizedType ptype = (ParameterizedType) type; - Type[] args = ptype.getActualTypeArguments(); - - if (args == null || args.length != 2) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the Map " - + "type " + type + "; unexpected number of type " - + "arguments."); - } - - if (args[0] != String.class) { - throw new DynamoDbMappingException( - "Only Map is supported."); - } - - ArgumentMarshaller memberMarshaller = - memberMarshaller(args[1]); - - return new MapToMapMarshaller(memberMarshaller); - } - - private ArgumentMarshaller getObjectToMapMarshaller(Type type) { - Type localType = type; - if (localType instanceof ParameterizedType) { - localType = ((ParameterizedType) localType).getRawType(); - } - - if (!(localType instanceof Class)) { - throw new DynamoDbMappingException( - "Cannot convert " + type + " to a class"); - } - - Class clazz = (Class) localType; - if (StandardAnnotationMaps.of(clazz).attributeType() != DynamoDbAttributeType.M) { - throw new DynamoDbMappingException( - "Cannot marshall type " + type - + " without a custom marshaler or @DynamoDBDocument " - + "annotation."); - } - - return new ObjectToMapMarshaller(this); - } - - @Override - public Object unconvert( - Method getter, - Method setter, - AttributeValue value) { - - ArgumentUnmarshaller unmarshaller = getUnmarshaller(getter, setter); - return unmarshall(unmarshaller, setter, value); - } - - @Override - public T unconvert( - Class clazz, - Map value) { - - T result = createObject(clazz); - if (value == null || value.isEmpty()) { - return result; - } - - for (final Bean bean : StandardBeanProperties.of(clazz).map().values()) { - AttributeValue av = value.get(bean.properties().attributeName()); - if (av != null) { - ArgumentUnmarshaller unmarshaller = getUnmarshaller(bean.type().getter(), bean.type().setter()); - Object unmarshalled = unmarshall(unmarshaller, bean.type().setter(), av); - bean.reflect().set(result, unmarshalled); - } - } - - return result; - } - - private ArgumentUnmarshaller getUnmarshaller( - Method getter, - Method setter) { - - ArgumentUnmarshaller unmarshaller = - unmarshallerSet.getUnmarshaller(getter, setter); - - unmarshaller = augment( - setter.getGenericParameterTypes()[0], unmarshaller); - - return new NullableUnmarshaller(unmarshaller); - } - - private ArgumentUnmarshaller memberUnmarshaller(Type type) { - ArgumentUnmarshaller unmarshaller = - unmarshallerSet.memberUnmarshaller(type); - - unmarshaller = augment(type, unmarshaller); - - return new NullableUnmarshaller(unmarshaller); - } - - private ArgumentUnmarshaller augment( - Type type, - ArgumentUnmarshaller unmarshaller) { - - // Inject our s3 client cache if it's an S3LinkUnmarshaller. - if (unmarshaller instanceof S3LinkUnmarshaller) { - return new S3LinkUnmarshaller(s3cc); - } - - // Inject an appropriate member-type unmarshaller if it's a list, - // object-set, or map unmarshaller. - if (unmarshaller instanceof ObjectSetUnmarshaller) { - return getObjectSetUnmarshaller(type); - } - - if (unmarshaller instanceof ListUnmarshaller) { - return listUnmarshaller(type); - } - - if (unmarshaller instanceof MapUnmarshaller) { - return mapUnmarshaller(type); - } - - // Inject ourselves to recursively unmarshall things if it's an - // ObjectUnmarshaller. - if (unmarshaller instanceof ObjectUnmarshaller) { - return getObjectUnmarshaller(type); - } - - return unmarshaller; - } - - private ArgumentUnmarshaller getObjectSetUnmarshaller(Type type) { - if (!(type instanceof ParameterizedType)) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the Set " - + "type " + type + ", which is not parameterized."); - } - - ParameterizedType ptype = (ParameterizedType) type; - Type[] args = ptype.getActualTypeArguments(); - - if (args == null || args.length != 1) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the Set " - + "type " + type + "; unexpected number of type " - + "arguments."); - } - - ArgumentUnmarshaller memberUnmarshaller = - memberUnmarshaller(args[0]); - - return new ObjectSetUnmarshaller(memberUnmarshaller); - } - - private ArgumentUnmarshaller listUnmarshaller(Type type) { - if (!(type instanceof ParameterizedType)) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the List " - + "type " + type + ", which is not parameterized."); - } - - ParameterizedType ptype = (ParameterizedType) type; - Type[] args = ptype.getActualTypeArguments(); - - if (args == null || args.length != 1) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the List " - + "type " + type + "; unexpected number of type " - + "arguments."); - } - - ArgumentUnmarshaller memberUnmarshaller = - memberUnmarshaller(args[0]); - - return new ListUnmarshaller(memberUnmarshaller); - } - - private ArgumentUnmarshaller mapUnmarshaller(Type type) { - if (!(type instanceof ParameterizedType)) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the Map " - + "type " + type + ", which is not parameterized."); - } - - ParameterizedType ptype = (ParameterizedType) type; - Type[] args = ptype.getActualTypeArguments(); - - if (args == null || args.length != 2) { - throw new DynamoDbMappingException( - "Cannot tell what type of objects belong in the Map " - + "type " + type + "; unexpected number of type " - + "arguments."); - } - - if (args[0] != String.class) { - throw new DynamoDbMappingException( - "Only Map is supported."); - } - - ArgumentUnmarshaller memberUnmarshaller = - memberUnmarshaller(args[1]); - - return new MapUnmarshaller(memberUnmarshaller); - } - - private ArgumentUnmarshaller getObjectUnmarshaller(Type type) { - Type localType = type; - if (localType instanceof ParameterizedType) { - localType = ((ParameterizedType) type).getRawType(); - } - - if (!(localType instanceof Class)) { - throw new DynamoDbMappingException( - "Cannot convert " + type + " to a class"); - } - - Class clazz = (Class) localType; - if (StandardAnnotationMaps.of(clazz).attributeType() != DynamoDbAttributeType.M) { - throw new DynamoDbMappingException( - "Cannot unmarshall to type " + type - + " without a custom marshaler or @DynamoDBDocument " - + "annotation."); - } - - return new ObjectUnmarshaller(this, clazz); - } - - } - - static final class V2MarshallerSet { - - private static List> marshallers() { - List> list = - new ArrayList>(); - - // Use the new V2 boolean marshallers. - addStandardDateMarshallers(list); - addV2BooleanMarshallers(list); - addStandardNumberMarshallers(list); - addStandardStringMarshallers(list); - addStandardBinaryMarshallers(list); - addStandardS3LinkMarshallers(list); - - // Add marshallers for the new list and map types. - list.add(Pair.of(List.class, CollectionToListMarshaller.instance())); - list.add(Pair.of(Map.class, MapToMapMarshaller.instance())); - - // Make sure I'm last since I'll catch anything. - list.add(Pair.of(Object.class, ObjectToMapMarshaller.instance())); - - return list; - } - - private static List> setMarshallers() { - List> list = - new ArrayList>(); - - // No more Set -> NS or Set -> SS marshallers - addStandardDateSetMarshallers(list); - addStandardNumberSetMarshallers(list); - addStandardStringSetMarshallers(list); - addStandardBinarySetMarshallers(list); - - // Make sure I'm last since I'll catch anything. - list.add(Pair.of( - Object.class, - CollectionToListMarshaller.instance())); - - return list; - } - } - - static final class V2CompatibleMarshallerSet { - - private static List> marshallers() { - List> list = - new ArrayList>(); - - // Keep the old v1 boolean marshallers for compatibility. - addStandardDateMarshallers(list); - addV1BooleanMarshallers(list); - addStandardNumberMarshallers(list); - addStandardStringMarshallers(list); - addStandardBinaryMarshallers(list); - addStandardS3LinkMarshallers(list); - - // Add marshallers for the new list and map types. - list.add(Pair.of(List.class, CollectionToListMarshaller.instance())); - list.add(Pair.of(Map.class, MapToMapMarshaller.instance())); - - // Make sure I'm last since I'll catch anything. - list.add(Pair.of(Object.class, ObjectToMapMarshaller.instance())); - - return list; - } - - private static List> setMarshallers() { - List> list = - new ArrayList>(); - - addStandardDateSetMarshallers(list); - addV1BooleanSetMarshallers(list); - addStandardNumberSetMarshallers(list); - addStandardStringSetMarshallers(list); - addStandardBinarySetMarshallers(list); - - // If all else fails, fall back to this default marshaler to - // retain backwards-compatible behavior. - list.add(Pair.of(Object.class, ObjectSetToStringSetMarshaller.instance())); - - return list; - } - } - - static final class V1MarshallerSet { - - private static List> marshallers() { - List> list = - new ArrayList>(); - - addStandardDateMarshallers(list); - addV1BooleanMarshallers(list); - addStandardNumberMarshallers(list); - addStandardStringMarshallers(list); - addStandardBinaryMarshallers(list); - addStandardS3LinkMarshallers(list); - - return list; - } - - private static List> setMarshallers() { - List> list = - new ArrayList>(); - - addStandardDateSetMarshallers(list); - addV1BooleanSetMarshallers(list); - addStandardNumberSetMarshallers(list); - addStandardStringSetMarshallers(list); - addStandardBinarySetMarshallers(list); - - // If all else fails, fall back to this default marshaler to - // retain backwards-compatible behavior. - list.add(Pair.of(Object.class, - ObjectSetToStringSetMarshaller.instance())); - - return list; - } - } - - private static class AbstractMarshallerSet implements MarshallerSet { - - private final List> marshallers; - private final List> setMarshallers; - - AbstractMarshallerSet( - List> marshallers, - List> setMarshallers) { - - this.marshallers = marshallers; - this.setMarshallers = setMarshallers; - } - - @Override - public ArgumentMarshaller marshaller(Method getter) { - Class returnType = getter.getReturnType(); - - if (Set.class.isAssignableFrom(returnType)) { - Class memberType = - unwrapGenericSetParam(getter.getGenericReturnType()); - - return set(getter, memberType); - } else { - return scalar(getter, returnType); - } - } - - @Override - public ArgumentMarshaller memberMarshaller(Type memberType) { - Class clazz = resolveClass(memberType); - if (Set.class.isAssignableFrom(clazz)) { - Class setMemberType = unwrapGenericSetParam(memberType); - return set(null, setMemberType); - } else { - return scalar(null, clazz); - } - } - - private ArgumentMarshaller scalar(Method getter, Class type) { - ArgumentMarshaller marshaller = find(type, marshallers); - if (marshaller == null) { - - String className = "?"; - String methodName = "?"; - if (getter != null) { - className = getter.getDeclaringClass().toString(); - methodName = getter.getName(); - } - - throw new DynamoDbMappingException( - "Cannot marshall return type " + type - + " of method " + className + "." + methodName - + " without a custom marshaler."); - } - - return marshaller; - } - - private ArgumentMarshaller set(Method getter, Class memberType) { - ArgumentMarshaller marshaller = find(memberType, setMarshallers); - if (marshaller == null) { - - String className = "?"; - String methodName = "?"; - if (getter != null) { - className = getter.getDeclaringClass().toString(); - methodName = getter.getName(); - } - - throw new DynamoDbMappingException( - "Cannot marshall return type Set<" + memberType - + "> of method " + className + "." + methodName - + " without a custom marshaller."); - } - - return marshaller; - } - } - - static class StandardUnmarshallerSet implements UnmarshallerSet { - - private final List> unmarshallers; - private final List> setUnmarshallers; - - StandardUnmarshallerSet() { - this(unmarshallers(), setUnmarshallers()); - } - - StandardUnmarshallerSet( - List> unmarshallers, - List> setUnmarshallers) { - - this.unmarshallers = unmarshallers; - this.setUnmarshallers = setUnmarshallers; - } - - private static List> unmarshallers() { - List> list = - new ArrayList>(); - - list.add(Pair.of(double.class, DoubleUnmarshaller.instance())); - list.add(Pair.of(Double.class, DoubleUnmarshaller.instance())); - - list.add(Pair.of(BigDecimal.class, - BigDecimalUnmarshaller.instance())); - list.add(Pair.of(BigInteger.class, - BigIntegerUnmarshaller.instance())); - - list.add(Pair.of(int.class, IntegerUnmarshaller.instance())); - list.add(Pair.of(Integer.class, IntegerUnmarshaller.instance())); - - list.add(Pair.of(float.class, FloatUnmarshaller.instance())); - list.add(Pair.of(Float.class, FloatUnmarshaller.instance())); - - list.add(Pair.of(byte.class, ByteUnmarshaller.instance())); - list.add(Pair.of(Byte.class, ByteUnmarshaller.instance())); - - list.add(Pair.of(long.class, LongUnmarshaller.instance())); - list.add(Pair.of(Long.class, LongUnmarshaller.instance())); - - list.add(Pair.of(short.class, ShortUnmarshaller.instance())); - list.add(Pair.of(Short.class, ShortUnmarshaller.instance())); - - list.add(Pair.of(boolean.class, BooleanUnmarshaller.instance())); - list.add(Pair.of(Boolean.class, BooleanUnmarshaller.instance())); - - list.add(Pair.of(Date.class, DateUnmarshaller.instance())); - list.add(Pair.of(Calendar.class, CalendarUnmarshaller.instance())); - - list.add(Pair.of(ByteBuffer.class, - ByteBufferUnmarshaller.instance())); - list.add(Pair.of(byte[].class, - ByteArrayUnmarshaller.instance())); - - list.add(Pair.of(S3Link.class, S3LinkUnmarshaller.instance())); - list.add(Pair.of(UUID.class, UuidUnmarshaller.instance())); - list.add(Pair.of(String.class, StringUnmarshaller.instance())); - - list.add(Pair.of(List.class, ListUnmarshaller.instance())); - list.add(Pair.of(Map.class, MapUnmarshaller.instance())); - - // Make sure I'm last since I'll catch all other types. - list.add(Pair.of(Object.class, ObjectUnmarshaller.instance())); - - return list; - } - - private static List> setUnmarshallers() { - List> list = - new ArrayList>(); - - list.add(Pair.of(double.class, DoubleSetUnmarshaller.instance())); - list.add(Pair.of(Double.class, DoubleSetUnmarshaller.instance())); - - list.add(Pair.of(BigDecimal.class, - BigDecimalSetUnmarshaller.instance())); - list.add(Pair.of(BigInteger.class, - BigIntegerSetUnmarshaller.instance())); - - list.add(Pair.of(int.class, IntegerSetUnmarshaller.instance())); - list.add(Pair.of(Integer.class, IntegerSetUnmarshaller.instance())); - - list.add(Pair.of(float.class, FloatSetUnmarshaller.instance())); - list.add(Pair.of(Float.class, FloatSetUnmarshaller.instance())); - - list.add(Pair.of(byte.class, ByteSetUnmarshaller.instance())); - list.add(Pair.of(Byte.class, ByteSetUnmarshaller.instance())); - - list.add(Pair.of(long.class, LongSetUnmarshaller.instance())); - list.add(Pair.of(Long.class, LongSetUnmarshaller.instance())); - - list.add(Pair.of(short.class, ShortSetUnmarshaller.instance())); - list.add(Pair.of(Short.class, ShortSetUnmarshaller.instance())); - - list.add(Pair.of(boolean.class, BooleanSetUnmarshaller.instance())); - list.add(Pair.of(Boolean.class, BooleanSetUnmarshaller.instance())); - - list.add(Pair.of(Date.class, DateSetUnmarshaller.instance())); - list.add(Pair.of(Calendar.class, - CalendarSetUnmarshaller.instance())); - - list.add(Pair.of(ByteBuffer.class, - ByteBufferSetUnmarshaller.instance())); - list.add(Pair.of(byte[].class, - ByteArraySetUnmarshaller.instance())); - - list.add(Pair.of(UUID.class, UuidSetUnmarshaller.instance())); - list.add(Pair.of(String.class, StringSetUnmarshaller.instance())); - - // Make sure I'm last since I'll catch all other types. - list.add(Pair.of(Object.class, ObjectSetUnmarshaller.instance())); - - return list; - } - - @Override - public ArgumentUnmarshaller getUnmarshaller( - Method getter, - Method setter) { - - if (setter.getParameterTypes().length != 1) { - throw new DynamoDbMappingException( - "Expected exactly one agument to " + setter); - } - Class paramType = setter.getParameterTypes()[0]; - - if (Set.class.isAssignableFrom(paramType)) { - - paramType = unwrapGenericSetParam( - setter.getGenericParameterTypes()[0]); - - return set(setter, paramType); - - } else { - return scalar(setter, paramType); - } - } - - @Override - public ArgumentUnmarshaller memberUnmarshaller(Type memberType) { - Class clazz = resolveClass(memberType); - if (Set.class.isAssignableFrom(clazz)) { - Class setMemberType = unwrapGenericSetParam(memberType); - return set(null, setMemberType); - } else { - return scalar(null, clazz); - } - } - - private ArgumentUnmarshaller set(Method setter, Class paramType) { - ArgumentUnmarshaller unmarshaller = - find(paramType, setUnmarshallers); - - String className = "?"; - String methodName = "?"; - if (setter != null) { - className = setter.getDeclaringClass().toString(); - methodName = setter.getName(); - } - - if (unmarshaller == null) { - throw new DynamoDbMappingException( - "Cannot unmarshall to parameter type Set<" - + paramType + "> of method " - + className + "." + methodName + " without a custom " - + "unmarshaler."); - } - - return unmarshaller; - } - - private ArgumentUnmarshaller scalar(Method setter, Class type) { - ArgumentUnmarshaller unmarshaller = find(type, unmarshallers); - - String className = "?"; - String methodName = "?"; - if (setter != null) { - className = setter.getDeclaringClass().toString(); - methodName = setter.getName(); - } - - if (unmarshaller == null) { - throw new DynamoDbMappingException( - "Cannot unmarshall to parameter type " + type - + "of method " + className + "." + methodName - + " without a custom unmarshaler."); - } - - return unmarshaller; - } - } - - private static class Pair { - - public final Class key; - public final T value; - - private Pair(Class key, T value) { - this.key = key; - this.value = value; - } - - public static Pair of( - Class key, - ArgumentMarshaller value) { - - return new Pair(key, value); - } - - public static Pair of( - Class key, - ArgumentUnmarshaller value) { - - return new Pair(key, value); - } - } - - static class AnnotationAwareMarshallerSet - implements MarshallerSet { - - private final MarshallerSet wrapped; - - AnnotationAwareMarshallerSet(MarshallerSet wrapped) { - this.wrapped = wrapped; - } - - @Override - public ArgumentMarshaller marshaller(Method getter) { - final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter, null); - final DynamoDbMarshalling marshalling = annotations.actualOf(DynamoDbMarshalling.class); - if (marshalling != null) { - return new CustomMarshaller(marshalling.marshallerClass()); - } else if (annotations.actualOf(DynamoDbNativeBoolean.class) != null) { - return BooleanToBooleanMarshaller.instance(); - } - return wrapped.marshaller(getter); - } - - @Override - public ArgumentMarshaller memberMarshaller(Type memberType) { - return wrapped.memberMarshaller(memberType); - } - } - - static class AnnotationAwareUnmarshallerSet - implements UnmarshallerSet { - - private final UnmarshallerSet wrapped; - - AnnotationAwareUnmarshallerSet(UnmarshallerSet wrapped) { - this.wrapped = wrapped; - } - - @Override - public ArgumentUnmarshaller getUnmarshaller( - Method getter, - Method setter) { - final StandardAnnotationMaps.FieldMap annotations = StandardAnnotationMaps.of(getter, null); - final DynamoDbMarshalling marshalling = annotations.actualOf(DynamoDbMarshalling.class); - if (marshalling != null) { - return new CustomUnmarshaller(getter.getReturnType(), marshalling.marshallerClass()); - } - return wrapped.getUnmarshaller(getter, setter); - } - - @Override - public ArgumentUnmarshaller memberUnmarshaller(Type c) { - return wrapped.memberUnmarshaller(c); - } - } - - static class CachingMarshallerSet implements MarshallerSet { - - private final Map cache = - new HashMap(); - - private final Map memberCache = - new HashMap(); - - private final MarshallerSet wrapped; - - CachingMarshallerSet(MarshallerSet wrapped) { - this.wrapped = wrapped; - } - - @Override - public ArgumentMarshaller marshaller(Method getter) { - synchronized (cache) { - ArgumentMarshaller marshaler = cache.get(getter); - if (marshaler != null) { - return marshaler; - } - - marshaler = wrapped.marshaller(getter); - cache.put(getter, marshaler); - return marshaler; - } - } - - @Override - public ArgumentMarshaller memberMarshaller(Type memberType) { - synchronized (memberCache) { - ArgumentMarshaller marshaller = memberCache.get(memberType); - if (marshaller != null) { - return marshaller; - } - - marshaller = wrapped.memberMarshaller(memberType); - memberCache.put(memberType, marshaller); - return marshaller; - } - } - } - - static class CachingUnmarshallerSet implements UnmarshallerSet { - - private final Map cache = - new HashMap(); - - private final Map memberCache = - new HashMap(); - - private final UnmarshallerSet wrapped; - - CachingUnmarshallerSet(UnmarshallerSet wrapped) { - this.wrapped = wrapped; - } - - @Override - public ArgumentUnmarshaller getUnmarshaller( - Method getter, - Method setter) { - - synchronized (cache) { - ArgumentUnmarshaller unmarshaler = cache.get(getter); - if (unmarshaler != null) { - return unmarshaler; - } - - unmarshaler = wrapped.getUnmarshaller(getter, setter); - cache.put(getter, unmarshaler); - return unmarshaler; - } - } - - @Override - public ArgumentUnmarshaller memberUnmarshaller(Type memberType) { - synchronized (memberCache) { - ArgumentUnmarshaller unmarshaller = memberCache.get(memberType); - if (unmarshaller != null) { - return unmarshaller; - } - - unmarshaller = wrapped.memberUnmarshaller(memberType); - memberCache.put(memberType, unmarshaller); - return unmarshaller; - } - } - } - - /** - * {@link AttributeValue} converter with {@link ItemConverter} - */ - static class ItemConverterRuleFactory implements RuleFactory { - private final RuleFactory typeConverters; - private final ItemConverter converter; - private final boolean customSchema; - - ItemConverterRuleFactory(DynamoDbMapperConfig config, S3Link.Factory s3Links, RuleFactory typeConverters) { - final ConversionSchema.Dependencies depends = - new ConversionSchema.Dependencies().with(S3ClientCache.class, s3Links.s3ClientCache()); - final ConversionSchema schema = config.getConversionSchema(); - - this.customSchema = (schema != V1 && schema != V2_COMPATIBLE && schema != V2); - this.converter = schema.getConverter(depends); - this.typeConverters = typeConverters; - } - - @Override - public Rule getRule(ConvertibleType type) { - if (customSchema && type.typeConverter() == null) { - return new ItemConverterRule(type); - } else { - return typeConverters.getRule(type); - } - } - - private final class ItemConverterRule implements Rule, DynamoDbTypeConverter { - private final ConvertibleType type; - - private ItemConverterRule(final ConvertibleType type) { - this.type = type; - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return true; - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return this; - } - - @Override - public DynamoDbAttributeType getAttributeType() { - try { - return converter.getFieldModel(type.getter()).attributeType(); - } catch (final DynamoDbMappingException no) { - // Ignored or expected. - } - return DynamoDbAttributeType.NULL; - } - - @Override - public AttributeValue convert(final V object) { - return converter.convert(type.getter(), object); - } - - @Override - public V unconvert(final AttributeValue object) { - return (V) converter.unconvert(type.getter(), type.setter(), object); - } - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionToAttributeValuesTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionToAttributeValuesTest.java deleted file mode 100644 index c17b4012a848..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConversionToAttributeValuesTest.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import java.util.Map; -import org.junit.Before; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class ConversionToAttributeValuesTest { - - private DynamoDbMapperModelFactory models; - private DynamoDbMapperConfig finalConfig; - - public static boolean equals(Object o1, Object o2) { - if (o1 == o2) { - return true; - } - if (o1 != null) { - return o1.equals(o2); - } - return false; - } - - public static int hash(Object... objs) { - int hash = 7; - for (int i = 0; i < objs.length; ++i) { - hash = hash * 31 + objs[i].hashCode(); - } - return hash; - } - - @Before - public void setUp() throws Exception { - finalConfig = new DynamoDbMapperConfig.Builder() - .withTypeConverterFactory(DynamoDbMapperConfig.DEFAULT.getTypeConverterFactory()) - .withConversionSchema(ConversionSchemas.V2) - .build(); - this.models = StandardModelFactories.of(S3Link.Factory.of(null)); - } - - @Test - public void converterFailsForSubProperty() throws Exception { - DynamoDbMapperTableModel tableModel = getTable(ConverterData.class); - Map withSubData = tableModel.convert(new ConverterData()); - assertEquals("bar", tableModel.unconvert(withSubData).subDocument().getaData().value()); - } - - private DynamoDbMapperTableModel getTable(Class clazz) { - return this.models.getTableFactory(finalConfig).getTable(clazz); - } - - @DynamoDbTable(tableName = "test") - public static class ConverterData { - - @DynamoDbTypeConverted(converter = CustomDataConverter.class) - CustomData customConverted; - @DynamoDbHashKey - private String key; - private ConverterSubDocument subDocument; - - public ConverterData() { - customConverted = new CustomData("foo"); - subDocument = new ConverterSubDocument(); - subDocument.setaData(new CustomData("bar")); - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public ConverterSubDocument subDocument() { - return subDocument; - } - - public void setSubDocument(ConverterSubDocument subProperty) { - this.subDocument = subProperty; - } - - public CustomData getCustomConverted() { - return customConverted; - } - - public void setCustomConverted(CustomData customConverted) { - this.customConverted = customConverted; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConverterData that = (ConverterData) o; - return ConversionToAttributeValuesTest.equals(subDocument, that.subDocument); - } - - @Override - public int hashCode() { - return ConversionToAttributeValuesTest.hash(subDocument); - } - - } - - @DynamoDbDocument - public static class ConverterSubDocument { - - @DynamoDbTypeConverted(converter = CustomDataConverter.class) - private CustomData aData; - - public CustomData getaData() { - return aData; - } - - public void setaData(CustomData aData) { - this.aData = aData; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ConverterSubDocument that = (ConverterSubDocument) o; - return ConversionToAttributeValuesTest.equals(aData, that.aData); - } - - @Override - public int hashCode() { - return ConversionToAttributeValuesTest.hash(aData); - } - } - - public static class CustomData { - - private final String value; - - public CustomData(String value) { - this.value = value; - } - - public String value() { - return value; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - CustomData that = (CustomData) o; - return ConversionToAttributeValuesTest.equals(value, that.value); - } - - @Override - public int hashCode() { - return ConversionToAttributeValuesTest.hash(value); - } - } - - public static class CustomDataConverter implements DynamoDbTypeConverter { - - public String convert(CustomData object) { - return object.value(); - } - - public CustomData unconvert(String object) { - return new CustomData(object); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConvertibleType.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConvertibleType.java deleted file mode 100644 index 8fdc4bfd8f39..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ConvertibleType.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardAnnotationMaps.TypedMap; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Scalar; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Vector; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -/** - * Generic type helper. - */ -@SdkInternalApi -final class ConvertibleType { - - private final DynamoDbTypeConverter typeConverter; - private final DynamoDbAttributeType attributeType; - private final ConvertibleType[] params; - private final Class targetType; - - @Deprecated - private final Method getter; - - @Deprecated - private final Method setter; - - /** - * Constructs a new parameter type. - */ - @SuppressWarnings("unchecked") - private ConvertibleType(Type genericType, TypedMap annotations, Method getter) { - this.typeConverter = annotations.typeConverter(); - this.attributeType = annotations.attributeType(); - - if (typeConverter != null) { - final ConvertibleType target = ConvertibleType.of(typeConverter); - this.targetType = target.targetType; - this.params = target.params; - } else if (genericType instanceof ParameterizedType) { - final Type[] paramTypes = ((ParameterizedType) genericType).getActualTypeArguments(); - this.targetType = annotations.targetType(); - this.params = new ConvertibleType[paramTypes.length]; - for (int i = 0; i < paramTypes.length; i++) { - this.params[i] = ConvertibleType.of(paramTypes[i]); - } - } else { - this.targetType = annotations.targetType(); - this.params = new ConvertibleType[0]; - } - - this.setter = getter == null ? null : StandardBeanProperties.MethodReflect.setterOf(getter); - this.getter = getter; - } - - /** - * Returns the conversion type for the method and annotations. - */ - static ConvertibleType of(Method getter, TypedMap annotations) { - return new ConvertibleType(getter.getGenericReturnType(), annotations, getter); - } - - /** - * Returns the conversion type for the converter. - */ - private static ConvertibleType of(final DynamoDbTypeConverter converter) { - final Class clazz = converter.getClass(); - if (!clazz.isInterface()) { - for (Class c = clazz; Object.class != c; c = c.getSuperclass()) { - for (final Type genericType : c.getGenericInterfaces()) { - final ConvertibleType type = ConvertibleType.of(genericType); - if (type.is(DynamoDbTypeConverter.class)) { - if (type.params.length == 2 && type.param(0).targetType() != Object.class) { - return type.param(0); - } - } - } - } - final ConvertibleType type = ConvertibleType.of(clazz.getGenericSuperclass()); - if (type.is(DynamoDbTypeConverter.class)) { - if (type.params.length > 0 && type.param(0).targetType() != Object.class) { - return type.param(0); - } - } - } - throw new DynamoDbMappingException("could not resolve type of " + clazz); - } - - /** - * Returns the conversion type for the generic type. - */ - private static ConvertibleType of(Type genericType) { - final Class targetType; - if (genericType instanceof Class) { - targetType = (Class) genericType; - } else if (genericType instanceof ParameterizedType) { - targetType = (Class) ((ParameterizedType) genericType).getRawType(); - } else if (genericType.toString().equals("byte[]")) { - targetType = (Class) byte[].class; - } else { - targetType = (Class) Object.class; - } - final TypedMap annotations = StandardAnnotationMaps.of(targetType); - return new ConvertibleType(genericType, annotations, null); - } - - /** - * Gets the target custom type-converter. - */ - DynamoDbTypeConverter typeConverter() { - return (DynamoDbTypeConverter) this.typeConverter; - } - - /** - * Gets the overriding attribute type. - */ - DynamoDbAttributeType attributeType() { - return this.attributeType; - } - - /** - * Gets the getter method. - */ - @Deprecated - Method getter() { - return this.getter; - } - - /** - * Gets the setter method. - */ - @Deprecated - Method setter() { - return this.setter; - } - - /** - * Gets the scalar parameter types. - */ - ConvertibleType param(final int index) { - return this.params.length > index ? (ConvertibleType) this.params[index] : null; - } - - /** - * Returns true if the types match. - */ - boolean is(ScalarAttributeType scalarAttributeType, Vector vector) { - return param(0) != null && param(0).is(scalarAttributeType) && is(vector); - } - - /** - * Returns true if the types match. - */ - boolean is(ScalarAttributeType scalarAttributeType) { - return Scalar.of(targetType()).is(scalarAttributeType); - } - - /** - * Returns true if the types match. - */ - boolean is(Scalar scalar) { - return scalar.is(targetType()); - } - - /** - * Returns true if the types match. - */ - boolean is(Vector vector) { - return vector.is(targetType()); - } - - /** - * Returns true if the types match. - */ - boolean is(Class type) { - return type.isAssignableFrom(targetType()); - } - - /** - * Gets the raw scalar type. - */ - Class targetType() { - return this.targetType; - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(targetType().getSimpleName()); - if (this.params.length > 0) { - builder.append("<"); - for (int i = 0; i < this.params.length; i++) { - builder.append(i == 0 ? "" : ",").append(this.params[i]); - } - builder.append(">"); - } - return builder.toString(); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDb.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDb.java deleted file mode 100644 index 752f78a75661..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDb.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to mark other annotations as being part of DynamoDB. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -public @interface DynamoDb { -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAttribute.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAttribute.java deleted file mode 100644 index 2204f11207e9..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAttribute.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Interface for marking a class property as an attribute in a DynamoDB table. - * Applied to the getter method or the class field for a modeled property. If - * the annotation is applied directly to the class field, the corresponding - * getter and setter must be declared in the same class. - *

    - * This annotation is optional when the name of the DynamoDB attribute matches - * the name of the property declared in the class. When they differ, use this - * annotation with the attributeName() parameter to specify which DynamoDB - * attribute this property corresponds to. Furthermore, the - * {@link DynamoDbMapper} class assumes Java naming conventions, and will - * lower-case the first character of a getter method's property name to - * determine the name of the property. E.g., a method value() will map to the - * DynamoDB attribute "value". Similarly, a method isValid() maps to the - * DynamoDB attribute "valid". - *

    - * Even getter method not marked with this annotation are assumed to be modeled - * properties, unless marked with {@link DynamoDbIgnore}. - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbAttribute { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; - - /** - * Optional parameter when using {@link DynamoDbFlattened}; identifies - * the field/property name on the target class to map as the attribute. - * @see DynamoDbFlattened - */ - String mappedBy() default ""; -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerateStrategy.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerateStrategy.java deleted file mode 100644 index 085c350c7890..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerateStrategy.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -/** - * Enumeration of possible auto-generation strategies. - * @see DynamoDbAutoGeneratedTimestamp - */ -public enum DynamoDbAutoGenerateStrategy { - - /** - * Instructs to always generate both on create and update. - */ - ALWAYS, - - /** - * Instructs to generate on create only. - */ - CREATE; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerated.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerated.java deleted file mode 100644 index fb4638496d31..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerated.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to mark a property as using a custom auto-generator. - * - *

    May be annotated on a user-defined annotation to pass additional - * properties to the {@link DynamoDbAutoGenerator}.

    - * - *
    - * @DynamoDBHashKey
    - * @CustomGeneratedKey(prefix="test-") //<- user-defined annotation
    - * public String getKey()
    - * 
    - * - *

    Where,

    - *
    - * @DynamoDBAutoGenerated(generator=CustomGeneratedKey.Generator.class)
    - * @Retention(RetentionPolicy.RUNTIME)
    - * @Target({ElementType.METHOD})
    - * public @interface CustomGeneratedKey {
    - *     String prefix() default "";
    - *
    - *     public static class Generator implements DynamoDBAutoGenerator<String> {
    - *         private final String prefix;
    - *         public Generator(final Class<String> targetType, final CustomGeneratedKey annotation) {
    - *             this.prefix = annotation.prefix();
    - *         }
    - *         public Generator() { //<- required if annotating directly
    - *             this.prefix = "";
    - *         }
    - *         @Override
    - *         public DynamoDBAutoGenerateStrategy getGenerateStrategy() {
    - *             return DynamoDBAutoGenerateStrategy.CREATE;
    - *         }
    - *         @Override
    - *         public final String generate(final String currentValue) {
    - *             return prefix + UUID.randomUUID.toString();
    - *         }
    - *     }
    - * }
    - * 
    - * - *

    Alternately, the property/field may be annotated directly (which requires - * the generator to provide a default constructor),

    - *
    - * @DynamoDBAutoGenerated(generator=CustomGeneratedKey.Generator.class)
    - * public String getKey()
    - * 
    - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbAutoGenerated { - - /** - * The auto-generator class for this property. - */ - @SuppressWarnings("rawtypes") - Class generator(); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedDefault.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedDefault.java deleted file mode 100644 index 6d67662b062d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedDefault.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to assign a default value on creation if value is null. - * - *
    - * @DynamoDBAutoGeneratedDefault("OPEN")
    - * public String status()
    - * 
    - * - *

    Only compatible with standard string types.

    - * - */ -@DynamoDb -@DynamoDbAutoGenerated(generator = DynamoDbAutoGeneratedDefault.Generator.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbAutoGeneratedDefault { - - /** - * The default value. - */ - String value(); - - - /** - * Default generator. - */ - final class Generator extends DynamoDbAutoGenerator.AbstractGenerator { - private final DynamoDbTypeConverter converter; - private final String defaultValue; - - Generator(Class targetType, DynamoDbAutoGeneratedDefault annotation) { - super(DynamoDbAutoGenerateStrategy.CREATE); - this.converter = StandardTypeConverters.factory().getConverter(targetType, String.class); - this.defaultValue = annotation.value(); - } - - @Override - public T generate(T currentValue) { - return converter.convert(defaultValue); - } - } - - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedKey.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedKey.java deleted file mode 100644 index 64d4d0bc4e5b..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedKey.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for marking a hash key or range key property in a class to - * auto-generate this key. Only String typed keys can be auto generated, and are - * given a random UUID. The annotation can be applied to either the getter - * method or the class field for the auto-generated key property. If the - * annotation is applied directly to the class field, the corresponding getter - * and setter must be declared in the same class. This annotation can be applied - * to both primary and index keys. - * - * @see DynamoDbGeneratedUuid - * @see java.util.UUID - */ -@DynamoDbGeneratedUuid(DynamoDbAutoGenerateStrategy.CREATE) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbAutoGeneratedKey { - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedTimestamp.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedTimestamp.java deleted file mode 100644 index 553573def7b8..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGeneratedTimestamp.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Date; - -/** - * Annotation for auto-generating a date/timestamp. - * - *
    - * @DynamoDBAutoGeneratedTimestamp(strategy=DynamoDBAutoGenerateStrategy.CREATE)
    - * public Date getCreatedDate() { return createdDate; }
    - * public void setCreatedDate(Date createdDate) { this.createdDate = createdDate; }
    - *
    - * @DynamoDBAutoGeneratedTimestamp(strategy=DynamoDBAutoGenerateStrategy.ALWAYS)
    - * public Date lastUpdatedDate() { return lastUpdatedDate; }
    - * public void setLastUpdatedDate(Date lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; }
    - * 
    - * - *

    Supports the standard {@link Date} type-conversions; such as - * {@link java.util.Calendar}, {@link Long}.

    - * - *

    Primitives such as {@code long} are not supported since the unset - * (or null) state can't be detected.

    - * - *

    Compatible with {@link DynamoDbTypeConvertedTimestamp}

    - */ -@DynamoDbAutoGenerated(generator = DynamoDbAutoGeneratedTimestamp.Generator.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbAutoGeneratedTimestamp { - - /** - * The auto-generation strategy; default is {@code ALWAYS}. - * @see DynamoDbAutoGenerateStrategy - */ - DynamoDbAutoGenerateStrategy strategy() default DynamoDbAutoGenerateStrategy.ALWAYS; - - /** - * Default generator. - */ - final class Generator extends DynamoDbAutoGenerator.AbstractGenerator { - private final DynamoDbTypeConverter converter; - - Generator(Class targetType, DynamoDbAutoGeneratedTimestamp annotation) { - super(annotation.strategy()); - this.converter = StandardTypeConverters.factory().getConverter(targetType, Date.class); - } - - @Override - public T generate(T currentValue) { - return converter.convert(new Date()); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerator.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerator.java deleted file mode 100644 index 56c1dd17b6ac..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbAutoGenerator.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * Generator interface for auto-generating attribute values. - * - *

    Auto-generation may be controlled by {@link DynamoDbAutoGenerateStrategy}, - - *

    {@link DynamoDbAutoGenerateStrategy#CREATE}, instructs to generate when - * creating the item. The mapper, determines an item is new, or overwriting, - * if it's current value is {@code null}. There is a limitiation when performing - * partial updates using either, - * {@link DynamoDbMapperConfig.SaveBehavior#UPDATE_SKIP_NULL_ATTRIBUTES}, or - * {@link DynamoDbMapperConfig.SaveBehavior#APPEND_SET}. A new value will only - * be generated if the mapper is also generating the key.

    - * - *

    {@link DynamoDbAutoGenerateStrategy#ALWAYS}, instructs to always generate - * a new value, applied on any save or batch write operation. - * - *

    May be used in combination with {@link DynamoDbAutoGenerated}.

    - * - * @param The object's field/property value type. - * - * @see DynamoDbAutoGenerated - */ -public interface DynamoDbAutoGenerator { - - /** - * Gets the auto-generate strategy. - */ - DynamoDbAutoGenerateStrategy getGenerateStrategy(); - - /** - * Generates a new value given the current value (or null) if applicable. - */ - T generate(T currentValue); - - /** - * A generator which holds the {@link DynamoDbAutoGenerateStrategy}. - */ - @SdkInternalApi - abstract class AbstractGenerator implements DynamoDbAutoGenerator { - private final DynamoDbAutoGenerateStrategy strategy; - - protected AbstractGenerator(DynamoDbAutoGenerateStrategy strategy) { - this.strategy = strategy; - } - - @Override - public DynamoDbAutoGenerateStrategy getGenerateStrategy() { - return this.strategy; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbConvertedBool.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbConvertedBool.java deleted file mode 100644 index 85020102d6a0..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbConvertedBool.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to convert a {@link Boolean} to the DynamoDB {@code S} type. - * - *
    - * @DynamoDBConvertedBool(DynamoDBConvertedBool.Format.Y_N)
    - * public boolean isTesting()
    - * 
    - * - *

    The standard V1 and V2 compatible conversion schemas will, by default, - * serialize booleans using the DynamoDB {@code N} type, with a value of '1' - * representing 'true' and a value of '0' representing 'false'. To force the - * {@code N} conversion in other schemas, - *

    - * @DynamoDBTyped(DynamoDBAttributeType.N)
    - * public boolean isTesting()
    - * 
    - * - *

    The standard V2 conversion schema will by default serialize booleans - * natively using the DynamoDB {@code BOOL} type. To force the native - * {@code BOOL} conversion in other schemas, - *

    - * @DynamoDBTyped(DynamoDBAttributeType.BOOL)
    - * public boolean isTesting()
    - * 
    - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@DynamoDbTypeConverted(converter = DynamoDbConvertedBool.Converter.class) -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.S) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbConvertedBool { - - /** - * The format type for converting to and from {@link String}. - */ - Format value(); - - - - /** - * Enumeration of the supported format options. - */ - enum Format { - true_false, T_F, Y_N - } - - /** - * Boolean type converter. - */ - final class Converter implements DynamoDbTypeConverter { - private final String valueTrue; - private final String valueFalse; - - Converter(Class targetType, DynamoDbConvertedBool annotation) { - this.valueTrue = annotation.value().name().split("_")[0]; - this.valueFalse = annotation.value().name().split("_")[1]; - } - - @Override - public String convert(final Boolean object) { - return Boolean.TRUE.equals(object) ? valueTrue : valueFalse; - } - - @Override - public Boolean unconvert(final String object) { - return valueTrue.equals(object) ? Boolean.TRUE : Boolean.FALSE; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDeleteExpression.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDeleteExpression.java deleted file mode 100644 index 63669c53b280..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDeleteExpression.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -/** - * Enables adding options to a delete operation. - * For example, you may want to delete only if an attribute has a particular value. - * @see DynamoDbMapper#delete(Object, DynamoDbDeleteExpression) - */ -public class DynamoDbDeleteExpression { - - /** Optional expected attributes. */ - private Map expectedAttributes; - - /** The logical operator on the expected attribute conditions. */ - private String conditionalOperator; - - /** - * A condition that must be satisfied in order for a conditional - * DeleteItem to succeed. - */ - private String conditionExpression; - - /** - * One or more substitution variables for simplifying complex - * expressions. - */ - private java.util.Map expressionAttributeNames; - - /** - * One or more values that can be substituted in an expression. - */ - private java.util.Map expressionAttributeValues; - - /** - * Gets the map of attribute names to expected attribute values to check on delete. - * - * @return The map of attribute names to expected attribute value conditions to check on delete - */ - public Map getExpected() { - return expectedAttributes; - } - - /** - * Sets the expected condition to the map of attribute names to expected attribute values given. - * - * @param expectedAttributes - * The map of attribute names to expected attribute value conditions to check on delete - */ - public void setExpected(Map expectedAttributes) { - this.expectedAttributes = expectedAttributes; - } - - /** - * Sets the expected condition to the map of attribute names to expected - * attribute values given and returns a pointer to this object for - * method-chaining. - * - * @param expectedAttributes - * The map of attribute names to expected attribute value - * conditions to check on delete - */ - public DynamoDbDeleteExpression withExpected(Map expectedAttributes) { - setExpected(expectedAttributes); - return this; - } - - /** - * Adds one entry to the expected conditions and returns a pointer to this - * object for method-chaining. - * - * @param attributeName - * The name of the attribute. - * @param expected - * The expected attribute value. - */ - public DynamoDbDeleteExpression withExpectedEntry(String attributeName, ExpectedAttributeValue expected) { - if (expectedAttributes == null) { - expectedAttributes = new HashMap(); - } - expectedAttributes.put(attributeName, expected); - return this; - } - - /** - * Returns the logical operator on the expected attribute conditions of this - * delete operation. - */ - public String getConditionalOperator() { - return conditionalOperator; - } - - /** - * Sets the logical operator on the expected attribute conditions of this - * delete operation. - */ - public void setConditionalOperator(String conditionalOperator) { - this.conditionalOperator = conditionalOperator; - } - - /** - * Sets the logical operator on the expected attribute conditions of this - * delete operation. - */ - public void setConditionalOperator(ConditionalOperator conditionalOperator) { - setConditionalOperator(conditionalOperator.toString()); - } - - /** - * Sets the logical operator on the expected attribute conditions of this - * delete operation and returns a pointer to this object for - * method-chaining. - */ - public DynamoDbDeleteExpression withConditionalOperator(String conditionalOperator) { - setConditionalOperator(conditionalOperator); - return this; - } - - /** - * Sets the logical operator on the expected attribute conditions of this - * delete operation and returns a pointer to this object for - * method-chaining. - */ - public DynamoDbDeleteExpression withConditionalOperator(ConditionalOperator conditionalOperator) { - return withConditionalOperator(conditionalOperator.toString()); - } - - /** - * A condition that must be satisfied in order for a conditional DeleteItem - * to succeed. - * - * @see DeleteItemRequest#getConditionExpression() - */ - public String getConditionExpression() { - return conditionExpression; - } - - /** - * A condition that must be satisfied in order for a conditional DeleteItem - * to succeed. - * - * @see DeleteItemRequest#setConditionExpression() - */ - public void setConditionExpression(String conditionExpression) { - this.conditionExpression = conditionExpression; - } - - /** - * A condition that must be satisfied in order for a conditional DeleteItem - * to succeed. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * - * @see DeleteItemRequest#withConditionExpression(String) - */ - public DynamoDbDeleteExpression withConditionExpression( - String conditionExpression) { - this.conditionExpression = conditionExpression; - return this; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @return One or more substitution variables for simplifying complex - * expressions. - * @see DeleteItemRequest#getExpressionAttributeNames() - */ - public java.util.Map getExpressionAttributeNames() { - - return expressionAttributeNames; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @param expressionAttributeNames - * One or more substitution variables for simplifying complex - * expressions. - * @see DeleteItemRequest#setExpressionAttributeNames(Map) - */ - public void setExpressionAttributeNames( - java.util.Map expressionAttributeNames) { - this.expressionAttributeNames = expressionAttributeNames; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @param expressionAttributeNames - * One or more substitution variables for simplifying complex - * expressions. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see DeleteItemRequest#withExpressionAttributeNames(Map) - */ - public DynamoDbDeleteExpression withExpressionAttributeNames( - java.util.Map expressionAttributeNames) { - setExpressionAttributeNames(expressionAttributeNames); - return this; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * The method adds a new key-value pair into ExpressionAttributeNames - * parameter, and returns a reference to this object so that method calls - * can be chained together. - * - * @param key - * The key of the entry to be added into - * ExpressionAttributeNames. - * @param value - * The corresponding value of the entry to be added into - * ExpressionAttributeNames. - * - * @see DeleteItemRequest#addExpressionAttributeNamesEntry(String, String) - */ - public DynamoDbDeleteExpression addExpressionAttributeNamesEntry( - String key, String value) { - if (null == this.expressionAttributeNames) { - this.expressionAttributeNames = new java.util.HashMap(); - } - if (this.expressionAttributeNames.containsKey(key)) { - throw new IllegalArgumentException("Duplicated keys (" + key + ") are provided."); - } - this.expressionAttributeNames.put(key, value); - return this; - } - - /** - * Removes all the entries added into ExpressionAttributeNames. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - */ - public DynamoDbDeleteExpression clearExpressionAttributeNamesEntries() { - this.expressionAttributeNames = null; - return this; - } - - /** - * One or more values that can be substituted in an expression. - * - * @return One or more values that can be substituted in an expression. - * @see DeleteItemRequest#getExpressionAttributeValues() - */ - public java.util.Map getExpressionAttributeValues() { - return expressionAttributeValues; - } - - /** - * One or more values that can be substituted in an expression. - * - * @param expressionAttributeValues - * One or more values that can be substituted in an expression. - * - * @see DeleteItemRequest#setExpressionAttributeValues(Map) - */ - public void setExpressionAttributeValues( - java.util.Map expressionAttributeValues) { - this.expressionAttributeValues = expressionAttributeValues; - } - - /** - * One or more values that can be substituted in an expression. - * - * @param expressionAttributeValues - * One or more values that can be substituted in an expression. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see DeleteItemRequest#withExpressionAttributeValues(Map) - */ - public DynamoDbDeleteExpression withExpressionAttributeValues( - java.util.Map expressionAttributeValues) { - setExpressionAttributeValues(expressionAttributeValues); - return this; - } - - /** - * One or more values that can be substituted in an expression. The method - * adds a new key-value pair into ExpressionAttributeValues parameter, and - * returns a reference to this object so that method calls can be chained - * together. - * - * @param key - * The key of the entry to be added into - * ExpressionAttributeValues. - * @param value - * The corresponding value of the entry to be added into - * ExpressionAttributeValues. - * - * @see DeleteItemRequest#addExpressionAttributeValuesEntry(String, - * AttributeValue) - */ - public DynamoDbDeleteExpression addExpressionAttributeValuesEntry( - String key, AttributeValue value) { - if (null == this.expressionAttributeValues) { - this.expressionAttributeValues = new java.util.HashMap(); - } - if (this.expressionAttributeValues.containsKey(key)) { - throw new IllegalArgumentException("Duplicated keys (" + key + ") are provided."); - } - this.expressionAttributeValues.put(key, value); - return this; - } - - /** - * Removes all the entries added into ExpressionAttributeValues. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - */ - public DynamoDbDeleteExpression clearExpressionAttributeValuesEntries() { - this.expressionAttributeValues = null; - return this; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDelimited.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDelimited.java deleted file mode 100644 index fe7f91664570..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDelimited.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.regex.Pattern; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardBeanProperties.Bean; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardBeanProperties.BeanMap; - -/** - * Annotation to convert an object into a single delimited {@link String} - * attribute. - * - *

    - * @DynamoDBDelimited(
    - *     attributeNames={"areaCode","exchange","subscriber"},
    - *     delimiter='-'
    - * )
    - * public PhoneNumber getPhoneNumber()
    - * 
    - * - *

    Where,

    - *
    - * public class PhoneNumber {
    - *     private String areaCode;
    - *     private String exchange;
    - *     private String subscriber;
    - *
    - *     public String getAreaCode() { return areaCode; }
    - *     public void setAreaCode(String areaCode) { this.areaCode = areaCode; }
    - *
    - *     public String getExchange() { return exchange; }
    - *     public void setExchange(String exchange) { this.exchange = exchange; }
    - *
    - *     public String subscriber() { return subscriber; }
    - *     public void setSubscriber(String subscriber) { this.subscriber = subscriber; }
    - * }
    - * 
    - * - *

    Would write,

    - *
      - *
    • PhoneNumber("206","266","1000") = "206-266-1000"
    • - *
    • PhoneNumber("206",null,"1000") = "206--1000"
    • - *
    • PhoneNumber("206",null,null) = "206--"
    • - *
    • PhoneNumber(null,"266","1000") = "-266-1000"
    • - *
    • PhoneNumber(null,"266",null) = "-266-"
    • - *
    • PhoneNumber(null,null,"1000") = "--1000"
    • - *
    • PhoneNumber(null,null,null) = null
    • - *
    • null = null
    • - *
    - * - * Conversely, reading not fully formatted values from DynamoDB given, - *
      - *
    • "" = empty string not allowed by DDB but would produce empty object
    • - *
    • "--" = PhoneNumber(null,null,null)
    • - *
    • "-----" = PhoneNumber(null,null,null)
    • - *
    • "206" = PhoneNumber("206",null,null)
    • - *
    • "206-266" = PhoneNumber("206","266",null)
    • - *
    • "206-266-1000-1234-5678" = PhoneNumber("206","266","1000")
    • - *
    - * - *

    The converter does not protect against values which may also contain the - * delimiter. If more advanced conversion is required, consider implementing, - * a custom {@link DynamoDbTypeConverter}.

    - * - *

    New delimited values may always be appended to the string, however, there - * are some risks in distributed systems where, if one system has updated - * delimiting instructions and begins to persist new values, other systems, - * which also persist that same data, would effectively truncate it back to the - * original format.

    - * - *

    Auto-generated annotations are not supported on field/property.

    - * - *

    TYpe-converted annotations, annotated by {@link DynamoDbTypeConverted}, - * where the output type is {@link String} are supported. - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@DynamoDbTypeConverted(converter = DynamoDbDelimited.Converter.class) -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.S) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbDelimited { - - /** - * The delimiter for separating attribute values; default is |. - */ - char delimiter() default '|'; - - /** - * The ordered list of attribute/field names. - */ - String[] attributeNames(); - - /** - * Type converter for string delimited attributes. - */ - final class Converter implements DynamoDbTypeConverter { - private final Field[] fields; - private final Class targetType; - private final String delimiter; - - Converter(Class targetType, DynamoDbDelimited annotation) { - final BeanMap beans = new BeanMap(targetType, true); - - final String[] names = annotation.attributeNames(); - if (names.length <= 1) { - throw new DynamoDbMappingException(targetType + - " missing attributeNames in @DynamoDBDelimited; must specify two or " + - "more attribute names"); - } - - this.delimiter = String.valueOf(annotation.delimiter()); - this.fields = new Field[names.length]; - this.targetType = targetType; - - for (int i = 0; i < fields.length; i++) { - if (beans.containsKey(names[i]) == false) { - throw new DynamoDbMappingException(targetType + " does not map %s on model " + names[i]); - } - this.fields[i] = new Field(targetType, beans.get(names[i])); - } - } - - @Override - public String convert(final T object) { - final StringBuilder string = new StringBuilder(); - for (int i = 0; i < fields.length; i++) { - if (i > 0) { - string.append(delimiter); - } - final String value = fields[i].get(object); - if (value != null) { - if (value.contains(delimiter)) { - throw new DynamoDbMappingException(String.format( - "%s[%s] field value \"%s\" must not contain delimiter %s", - targetType, fields[i].bean.properties().attributeName(), value, delimiter - )); - } - string.append(value); - } - } - return string.length() < fields.length ? null : string.toString(); - } - - @Override - public T unconvert(final String string) { - final T object = StandardBeanProperties.DeclaringReflect.newInstance(targetType); - final String[] values = string.split(Pattern.quote(delimiter)); - for (int i = 0, its = Math.min(fields.length, values.length); i < its; i++) { - fields[i].set(object, values[i]); - } - return object; - } - - private static final class Field { - private final DynamoDbTypeConverter converter; - private final Bean bean; - - private Field(final Class type, final Bean bean) { - if (bean.type().typeConverter() == null) { - this.converter = StandardTypeConverters.factory().getConverter(String.class, bean.type().targetType()); - } else { - this.converter = bean.type().typeConverter(); - } - this.bean = bean; - } - - private String get(final T object) { - final V value = bean.reflect().get(object); - if (value == null) { - return null; - } - return converter.convert(value); - } - - private void set(final T object, final String string) { - if (!string.isEmpty()) { - final V value = converter.unconvert(string); - if (value != null) { - bean.reflect().set(object, value); - } - } - } - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDocument.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDocument.java deleted file mode 100644 index 3ba251997c4b..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbDocument.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that marks a class which can be serialized to a DynamoDB - * document or sub-document. Behaves exactly the same as {@link DynamoDbTable}, - * but without requiring you to specify a {@code tableName}. - */ -@DynamoDb -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.M) -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Inherited -public @interface DynamoDbDocument { - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbFlattened.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbFlattened.java deleted file mode 100644 index b8fec9e0febb..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbFlattened.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for flattening a complex type. - * - *
    - * @DynamoDBFlattened(attributes={
    - *     @DynamoDBAttribute(mappedBy="start", attributeName="effectiveStartDate"),
    - *     @DynamoDBAttribute(mappedBy="end", attributeName="effectiveEndDate")})
    - * public DateRange getEffectiveRange() { return effectiveRange; }
    - * public void setEffectiveRange(DateRange effectiveRange) { this.effectiveRange = effectiveRange; }
    - *
    - * @DynamoDBFlattened(attributes={
    - *     @DynamoDBAttribute(mappedBy="start", attributeName="extensionstartDate"),
    - *     @DynamoDBAttribute(mappedBy="end", attributeName="extensionEndDate")})
    - * public DateRange getExtensionRange() { return extensionRange; }
    - * public void setExtensionRange(DateRange extensionRange) { this.extensionRange = extensionRange; }
    - * 
    - * - *

    Where,

    - *
    - * public class DateRange {
    - *     private Date start;
    - *     private Date end;
    - *
    - *     public Date start() { return start; }
    - *     public void setStart(Date start) { this.start = start; }
    - *
    - *     public Date getEnd() { return end; }
    - *     public void setEnd(Date end) { this.end = end; }
    - * }
    - * 
    - * - *

    Attributes defined within the complex type may also be annotated.

    - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbFlattened { - - /** - * Indicates the attributes that should be flattened. - */ - DynamoDbAttribute[] attributes() default {}; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbGeneratedUuid.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbGeneratedUuid.java deleted file mode 100644 index d3dd5dec6ddc..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbGeneratedUuid.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.UUID; - -/** - * Annotation for auto-generating a {@link UUID}. - * - *
    - * @DynamoDBGeneratedUuid(DynamoDBAutoGenerateStrategy.CREATE)
    - * public UUID getKey()
    - * 
    - * - *

    When applied to a key field, only the strategy - * {@link DynamoDbAutoGenerateStrategy#CREATE} is supported.

    - * - *

    The short-formed {@link DynamoDbAutoGeneratedKey} may also be used for - * create only.

    - * - *

    May be used as a meta-annotation.

    - * - * @see java.util.UUID - */ -@DynamoDb -@DynamoDbAutoGenerated(generator = DynamoDbGeneratedUuid.Generator.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbGeneratedUuid { - - /** - * The auto-generation strategy. - */ - DynamoDbAutoGenerateStrategy value(); - - /** - * Default generator. - */ - final class Generator extends DynamoDbAutoGenerator.AbstractGenerator { - private final DynamoDbTypeConverter converter; - - Generator(Class targetType, DynamoDbGeneratedUuid annotation) { - super(annotation.value()); - this.converter = StandardTypeConverters.factory().getConverter(targetType, UUID.class); - } - - @Override - public T generate(final T currentValue) { - return converter.convert(UUID.randomUUID()); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbHashKey.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbHashKey.java deleted file mode 100644 index fcb5473b6684..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbHashKey.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for marking a property as the hash key for a modeled class. - * Applied to the getter method or the class field for a hash key property. If - * the annotation is applied directly to the class field, the corresponding - * getter and setter must be declared in the same class. - *

    - * This annotation is required. - */ -@DynamoDb -@DynamoDbKeyed(software.amazon.awssdk.services.dynamodb.model.KeyType.HASH) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbHashKey { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIgnore.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIgnore.java deleted file mode 100644 index 3768eafb0645..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIgnore.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for marking a class property as non-modeled. Applied to the getter - * method or the class field for a non-modeled property. If the annotation is - * applied directly to the class field, the corresponding getter and setter must - * be declared in the same class. - *

    - * All getter methods not marked with this annotation are assumed to be modeled - * properties and included in any save() requests. - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbIgnore { - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIndexHashKey.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIndexHashKey.java deleted file mode 100644 index 820dabe58360..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIndexHashKey.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for marking a property in a class as the attribute to be used as - * the hash key for one or more global secondary indexes on a DynamoDB table. - * Applied to the getter method or the class field for the index hash key - * property. If the annotation is applied directly to the class field, the - * corresponding getter and setter must be declared in the same class. - *

    - * This annotation is required if this attribute will be used as index key for - * item queries. - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbIndexHashKey { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; - - /** - * Parameter for the name of the global secondary index. - *

    - * This is required if this attribute is the index key for only one global secondary - * index. - */ - String globalSecondaryIndexName() default ""; - - /** - * Parameter for the names of the global secondary indexes. - * This is required if this attribute is the index key for multiple global secondary - * indexes. - */ - String[] globalSecondaryIndexNames() default {}; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIndexRangeKey.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIndexRangeKey.java deleted file mode 100644 index f107796dbb63..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbIndexRangeKey.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for marking a property in a class as the attribute to be used as - * range key for one or more local secondary indexes on a DynamoDB table. - * Applied to the getter method or the class field for the indexed range key - * property. If the annotation is applied directly to the class field, the - * corresponding getter and setter must be declared in the same class. - *

    - * This annotation is required if this attribute will be used as index key for - * item queries. - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbIndexRangeKey { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; - - /** - * Parameter for the name of the local secondary index. - *

    - * This is required if this attribute is the index key for only one local secondary - * index. - */ - String localSecondaryIndexName() default ""; - - /** - * Parameter for the names of the local secondary indexes. - *

    - * This is required if this attribute is the index key for multiple local secondary - * indexes. - */ - String[] localSecondaryIndexNames() default {}; - - /** - * Parameter for the name of the global secondary index. - *

    - * This is required if this attribute is the index key for only one global secondary - * index. - */ - String globalSecondaryIndexName() default ""; - - /** - * Parameter for the names of the global secondary indexes. - *

    - * This is required if this attribute is the index key for multiple global secondary - * indexes. - */ - String[] globalSecondaryIndexNames() default {}; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbKeyed.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbKeyed.java deleted file mode 100644 index f22b8a490de1..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbKeyed.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import software.amazon.awssdk.services.dynamodb.model.KeyType; - -/** - * Annotation for marking a property a key for a modeled class. - * - *

    - * @DynamoDBKeyed(KeyType.HASH)
    - * public UUID getKey()
    - * 
    - * - *

    Alternately, the short-formed {@link DynamoDbHashKey}, and - * {@link DynamoDbRangeKey} may be used directly on the field/getter.

    - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbKeyed { - - /** - * The primary key type; either {@code HASH} or {@code RANGE}. - */ - KeyType value(); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapper.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapper.java deleted file mode 100644 index cf0ba352910e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapper.java +++ /dev/null @@ -1,2367 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static java.util.stream.Collectors.toMap; -import static software.amazon.awssdk.services.dynamodb.model.KeyType.HASH; -import static software.amazon.awssdk.services.dynamodb.model.KeyType.RANGE; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.awscore.AwsRequest; -import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.core.retry.RetryUtils; -import software.amazon.awssdk.core.util.SdkAutoConstructMap; -import software.amazon.awssdk.core.util.VersionInfo; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.BatchLoadRetryStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.BatchWriteRetryStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.ConsistentRead; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.model.AttributeAction; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemResponse; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; -import software.amazon.awssdk.services.dynamodb.model.PutRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; -import software.amazon.awssdk.services.dynamodb.model.ReturnValue; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; -import software.amazon.awssdk.services.dynamodb.model.Select; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * Object mapper for domain-object interaction with DynamoDB. - *

    - * To use, define a domain class that represents an item in a DynamoDB table and - * annotate it with the annotations found in the - * software.amazon.awssdk.services.dynamodbv2.datamodeling package. In order to allow the - * mapper to correctly persist the data, each modeled property in the domain - * class should be accessible via getter and setter methods, and each property - * annotation should be either applied to the getter method or the class field. - * A minimal example using getter annotations: - * - *

    - * @DynamoDBTable(tableName = "TestTable")
    - * public class TestClass {
    - *
    - *     private Long key;
    - *     private double rangeKey;
    - *     private Long version;
    - *
    - *     private Set<Integer> integerSetAttribute;
    - *
    - *     @DynamoDBHashKey
    - *     public Long getKey() {
    - *         return key;
    - *     }
    - *
    - *     public void setKey(Long key) {
    - *         this.key = key;
    - *     }
    - *
    - *     @DynamoDBRangeKey
    - *     public double getRangeKey() {
    - *         return rangeKey;
    - *     }
    - *
    - *     public void setRangeKey(double rangeKey) {
    - *         this.rangeKey = rangeKey;
    - *     }
    - *
    - *     @DynamoDBAttribute(attributeName = "integerSetAttribute")
    - *     public Set<Integer> getIntegerAttribute() {
    - *         return integerSetAttribute;
    - *     }
    - *
    - *     public void setIntegerAttribute(Set<Integer> integerAttribute) {
    - *         this.integerSetAttribute = integerAttribute;
    - *     }
    - *
    - *     @DynamoDBVersionAttribute
    - *     public Long getVersion() {
    - *         return version;
    - *     }
    - *
    - *     public void setVersion(Long version) {
    - *         this.version = version;
    - *     }
    - * }
    - * 
    - *

    - * Save instances of annotated classes to DynamoDB, retrieve them, and delete - * them using the {@link DynamoDbMapper} class, as in the following example. - * - *

    - * DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
    - * Long hashKey = 105L;
    - * double rangeKey = 1.0d;
    - * TestClass obj = mapper.load(TestClass.class, hashKey, rangeKey);
    - * obj.getIntegerAttribute().add(42);
    - * mapper.save(obj);
    - * mapper.delete(obj);
    - * 
    - *

    - * If you don't have your DynamoDB table set up yet, you can use - * {@link DynamoDbMapper#generateCreateTableRequest(Class)} to construct the - * {@link CreateTableRequest} for the table represented by your annotated class. - * - *

    - * DynamoDbClient dynamoDBClient = new AmazonDynamoDbClient();
    - * DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
    - * CreateTableRequest req = mapper.generateCreateTableRequest(TestClass.class);
    - * // Table provision throughput is still required since it cannot be specified in your POJO
    - * req.setProvisionedThroughput(new ProvisionedThroughput(5L, 5L));
    - * // Fire off the CreateTableRequest using the low-level client
    - * dynamoDBClient.createTable(req);
    - * 
    - *

    - * When using the save, load, and delete methods, {@link DynamoDbMapper} will - * throw {@link DynamoDbMappingException}s to indicate that domain classes are - * incorrectly annotated or otherwise incompatible with this class. Service - * exceptions will always be propagated as {@link SdkClientException}, and - * DynamoDB-specific subclasses such as {@link ConditionalCheckFailedException} - * will be used when possible. - *

    - * This class is thread-safe and can be shared between threads. It's also very - * lightweight, so it doesn't need to be. - * - * @see DynamoDbTable - * @see DynamoDbHashKey - * @see DynamoDbRangeKey - * @see DynamoDbAutoGeneratedKey - * @see DynamoDbAttribute - * @see DynamoDbVersionAttribute - * @see DynamoDbIgnore - * @see DynamoDbMarshalling - * @see DynamoDbMapperConfig - */ -public class DynamoDbMapper extends AbstractDynamoDbMapper { - - /** - * The max back off time for batch get. The configuration for batch write - * has been moved to DynamoDBMapperConfig - */ - protected static final long MAX_BACKOFF_IN_MILLISECONDS = 1000 * 3L; - /** The max number of items allowed in a BatchWrite request. */ - protected static final int MAX_ITEMS_PER_BATCH = 25; - /** - * This retry count is applicable only when every batch get item request - * results in no data retrieved from server and the un processed keys is - * same as request items - */ - protected static final int BATCH_GET_MAX_RETRY_COUNT_ALL_KEYS = 5; - /** - * User agent for requests made using the {@link DynamoDbMapper}. - */ - private static final String USER_AGENT_NAME = - DynamoDbMapper.class.getName(); - private static final String USER_AGENT_BATCH_OPERATION_NAME = - DynamoDbMapper.class.getName() + "_batch_operation"; - private static final Logger log = LoggerFactory.getLogger(DynamoDbMapper.class); - private final DynamoDbClient db; - private final DynamoDbMapperModelFactory models; - private final S3Link.Factory s3Links; - private final AttributeTransformer transformer; - - /** - * Constructs a new mapper with the service object given, using the default - * configuration. - * - * @param dynamoDb - * The service object to use for all service calls. - * @see DynamoDbMapperConfig#DEFAULT - */ - public DynamoDbMapper(final DynamoDbClient dynamoDb) { - this(dynamoDb, DynamoDbMapperConfig.DEFAULT, null, null); - } - - - /** - * Constructs a new mapper with the service object and configuration given. - * - * @param dynamoDb - * The service object to use for all service calls. - * @param config - * The default configuration to use for all service calls. It can - * be overridden on a per-operation basis. - */ - public DynamoDbMapper( - final DynamoDbClient dynamoDb, - final DynamoDbMapperConfig config) { - - this(dynamoDb, config, null, null); - } - - /** - * Constructs a new mapper with the service object and S3 client cache - * given, using the default configuration. - * - * @param ddb - * The service object to use for all service calls. - * @param s3CredentialProvider - * The credentials provider for accessing S3. - * Relevant only if {@link S3Link} is involved. - * @see DynamoDbMapperConfig#DEFAULT - */ - public DynamoDbMapper( - final DynamoDbClient ddb, - final AwsCredentialsProvider s3CredentialProvider) { - - this(ddb, DynamoDbMapperConfig.DEFAULT, s3CredentialProvider); - } - - /** - * Constructs a new mapper with the given service object, configuration, - * and transform hook. - * - * @param dynamoDb - * the service object to use for all service calls - * @param config - * the default configuration to use for all service calls. It - * can be overridden on a per-operation basis - * @param transformer - * The custom attribute transformer to invoke when serializing or - * deserializing an object. - */ - public DynamoDbMapper( - final DynamoDbClient dynamoDb, - final DynamoDbMapperConfig config, - final AttributeTransformer transformer) { - - this(dynamoDb, config, transformer, null); - } - - /** - * Constructs a new mapper with the service object, configuration, and S3 - * client cache given. - * - * @param dynamoDb - * The service object to use for all service calls. - * @param config - * The default configuration to use for all service calls. It can - * be overridden on a per-operation basis. - * @param s3CredentialProvider - * The credentials provider for accessing S3. - * Relevant only if {@link S3Link} is involved. - */ - public DynamoDbMapper( - final DynamoDbClient dynamoDb, - final DynamoDbMapperConfig config, - final AwsCredentialsProvider s3CredentialProvider) { - - this(dynamoDb, config, null, validate(s3CredentialProvider)); - } - - /** - * Constructor with all parameters. - * - * @param dynamoDb - * The service object to use for all service calls. - * @param config - * The default configuration to use for all service calls. It can - * be overridden on a per-operation basis. - * @param transformer - * The custom attribute transformer to invoke when serializing or - * deserializing an object. - * @param s3CredentialsProvider - * The credentials provider for accessing S3. - * Relevant only if {@link S3Link} is involved. - */ - public DynamoDbMapper( - final DynamoDbClient dynamoDb, - final DynamoDbMapperConfig config, - final AttributeTransformer transformer, - final AwsCredentialsProvider s3CredentialsProvider) { - super(config); - - failFastOnIncompatibleSubclass(getClass()); - - this.db = dynamoDb; - this.transformer = transformer; - - this.s3Links = S3Link.Factory.of(s3CredentialsProvider); - - this.models = StandardModelFactories.of(this.s3Links); - } - - /** - * Fail fast when trying to create a subclass of the DynamoDBMapper that - * attempts to override one of the old {@code transformAttributes} methods. - */ - private static void failFastOnIncompatibleSubclass(Class clazz) { - while (clazz != DynamoDbMapper.class) { - Class[] classOverride = new Class[] { - Class.class, - Map.class - }; - Class[] nameOverride = new Class[] { - String.class, - String.class, - Map.class - }; - - for (Method method : clazz.getDeclaredMethods()) { - if (method.getName().equals("transformAttributes")) { - Class[] params = method.getParameterTypes(); - if (Arrays.equals(params, classOverride) - || Arrays.equals(params, nameOverride)) { - - throw new IllegalStateException( - "The deprecated transformAttributes method is " - + "no longer supported as of 1.9.0. Use an " - + "AttributeTransformer to inject custom " - + "attribute transformation logic."); - } - } - } - - clazz = clazz.getSuperclass(); - } - } - - /** - * Throws an exception if the given credentials provider is {@code null}. - */ - private static AwsCredentialsProvider validate( - final AwsCredentialsProvider provider) { - if (provider == null) { - throw new IllegalArgumentException( - "s3 credentials provider must not be null"); - } - return provider; - } - - private static boolean isNullOrEmpty(Map map) { - return map == null || map.isEmpty(); - } - - /** - * Determnes if any of the primary keys require auto-generation. - */ - private static boolean anyKeyGeneratable( - final DynamoDbMapperTableModel model, - final T object, - final SaveBehavior saveBehavior) { - for (final DynamoDbMapperFieldModel field : model.keys()) { - if (canGenerate(model, object, saveBehavior, field)) { - return true; - } - } - return false; - } - - /** - * Determines if the mapping value can be auto-generated. - */ - private static boolean canGenerate( - final DynamoDbMapperTableModel model, - final T object, - final SaveBehavior saveBehavior, - final DynamoDbMapperFieldModel field) { - if (field.getGenerateStrategy() == null) { - return false; - } else if (field.getGenerateStrategy() == DynamoDbAutoGenerateStrategy.ALWAYS) { - return true; - } else if (field.get(object) != null) { - return false; - } else if (field.keyType() != null || field.indexed()) { - return true; - } else if (saveBehavior == SaveBehavior.CLOBBER) { - return true; - } else if (saveBehavior == SaveBehavior.UPDATE) { - return true; - } else if (anyKeyGeneratable(model, object, saveBehavior)) { - return true; - } - return false; - } - - /** - * Utility method for checking the validity of both hash and range key - * conditions. It also tries to infer the correct index name from the POJO - * annotation, if such information is not directly specified by the user. - * - * @param clazz - * The domain class of the queried items. - * @param queryRequest - * The QueryRequest object to be sent to service. - * @param hashKeyConditions - * All the hash key EQ conditions extracted from the POJO object. - * The mapper will choose one of them that could be applied together with - * the user-specified (if any) index name and range key conditions. Or it - * throws error if more than one conditions are applicable for the query. - * @param rangeKeyConditions - * The range conditions specified by the user. We currently only - * allow at most one range key condition. - */ - private static QueryRequest processKeyConditions( - QueryRequest queryRequest, - final DynamoDbQueryExpression expression, - final DynamoDbMapperTableModel model) { - // Hash key (primary or index) condition - final Map hashKeyConditions = new LinkedHashMap(); - if (expression.getHashKeyValues() != null) { - for (final DynamoDbMapperFieldModel field : model.fields()) { - if (field.keyType() == HASH || !field.globalSecondaryIndexNames(HASH).isEmpty()) { - final Object value = field.get(expression.getHashKeyValues()); - if (value != null) { - hashKeyConditions.put(field.name(), field.eq(value)); - } - } - } - } - - // Range key (primary or index) conditions - final Map rangeKeyConditions = expression.getRangeKeyConditions(); - - // There should be least one hash key condition. - final String keyCondExpression = queryRequest.keyConditionExpression(); - if (keyCondExpression == null) { - if (isNullOrEmpty(hashKeyConditions)) { - throw new IllegalArgumentException("Illegal query expression: No hash key condition is found in the query"); - } - } else { - if (!isNullOrEmpty(hashKeyConditions)) { - throw new IllegalArgumentException("Illegal query expression: Either the hash key conditions or the key " + - "condition expression must be specified but not both."); - } - if (!isNullOrEmpty(rangeKeyConditions)) { - throw new IllegalArgumentException("Illegal query expression: The range key conditions can only be specified " + - "when the key condition expression is not specified."); - } - // key condition expression is in use - return queryRequest; - } - // We don't allow multiple range key conditions. - if (rangeKeyConditions != null && rangeKeyConditions.size() > 1) { - throw new IllegalArgumentException( - "Illegal query expression: Conditions on multiple range keys (" - + rangeKeyConditions.keySet().toString() - + ") are found in the query. DynamoDB service only accepts up to ONE range key condition."); - } - final boolean hasRangeKeyCondition = (rangeKeyConditions != null) - && (!rangeKeyConditions.isEmpty()); - final String userProvidedIndexName = queryRequest.indexName(); - final String primaryHashKeyName = model.hashKey().name(); - - // First collect the names of all the global/local secondary indexes that could be applied to this query. - // If the user explicitly specified an index name, we also need to - // 1) check the index is applicable for both hash and range key conditions - // 2) choose one hash key condition if there are more than one of them - boolean hasPrimaryHashKeyCondition = false; - final Map> annotatedGsisOnHashKeys = new HashMap>(); - String hashKeyNameForThisQuery = null; - - boolean hasPrimaryRangeKeyCondition = false; - final Set annotatedLsisOnRangeKey = new HashSet(); - final Set annotatedGsisOnRangeKey = new HashSet(); - - // Range key condition - String rangeKeyNameForThisQuery = null; - if (hasRangeKeyCondition) { - for (String rangeKeyName : rangeKeyConditions.keySet()) { - rangeKeyNameForThisQuery = rangeKeyName; - - final DynamoDbMapperFieldModel rk = model.field(rangeKeyName); - - if (rk.keyType() == RANGE) { - hasPrimaryRangeKeyCondition = true; - } - - annotatedLsisOnRangeKey.addAll(rk.localSecondaryIndexNames()); - annotatedGsisOnRangeKey.addAll(rk.globalSecondaryIndexNames(RANGE)); - } - - if (!hasPrimaryRangeKeyCondition - && annotatedLsisOnRangeKey.isEmpty() - && annotatedGsisOnRangeKey.isEmpty()) { - throw new DynamoDbMappingException( - "The query contains a condition on a range key (" + - rangeKeyNameForThisQuery + ") " + - "that is not annotated with either @DynamoDBRangeKey or @DynamoDBIndexRangeKey."); - } - } - - final boolean userProvidedLsiWithRangeKeyCondition = (userProvidedIndexName != null) - && (annotatedLsisOnRangeKey.contains(userProvidedIndexName)); - final boolean hashOnlyLsiQuery = (userProvidedIndexName != null) - && (!hasRangeKeyCondition) - && model.localSecondaryIndex(userProvidedIndexName) != null; - final boolean userProvidedLsi = userProvidedLsiWithRangeKeyCondition || hashOnlyLsiQuery; - - final boolean userProvidedGsiWithRangeKeyCondition = (userProvidedIndexName != null) - && (annotatedGsisOnRangeKey.contains(userProvidedIndexName)); - final boolean hashOnlyGsiQuery = (userProvidedIndexName != null) - && (!hasRangeKeyCondition) - && model.globalSecondaryIndex(userProvidedIndexName) != null; - final boolean userProvidedGsi = userProvidedGsiWithRangeKeyCondition || hashOnlyGsiQuery; - - if (userProvidedLsi && userProvidedGsi) { - throw new DynamoDbMappingException( - "Invalid query: " + - "Index \"" + userProvidedIndexName + "\" " + - "is annotateded as both a LSI and a GSI for attribute."); - } - - // Hash key conditions - for (String hashKeyName : hashKeyConditions.keySet()) { - if (hashKeyName.equals(primaryHashKeyName)) { - hasPrimaryHashKeyCondition = true; - } - - final DynamoDbMapperFieldModel hk = model.field(hashKeyName); - - Collection annotatedGsiNames = hk.globalSecondaryIndexNames(HASH); - annotatedGsisOnHashKeys.put(hashKeyName, - annotatedGsiNames == null ? new HashSet<>() : new HashSet<>(annotatedGsiNames)); - - // Additional validation if the user provided an index name. - if (userProvidedIndexName != null) { - boolean foundHashKeyConditionValidWithUserProvidedIndex = false; - if (userProvidedLsi && hashKeyName.equals(primaryHashKeyName)) { - // found an applicable hash key condition (primary hash + LSI range) - foundHashKeyConditionValidWithUserProvidedIndex = true; - } else if (userProvidedGsi && - annotatedGsiNames != null && annotatedGsiNames.contains(userProvidedIndexName)) { - // found an applicable hash key condition (GSI hash + range) - foundHashKeyConditionValidWithUserProvidedIndex = true; - } - if (foundHashKeyConditionValidWithUserProvidedIndex) { - if (hashKeyNameForThisQuery != null) { - throw new IllegalArgumentException( - "Ambiguous query expression: More than one hash key EQ conditions (" + - hashKeyNameForThisQuery + ", " + hashKeyName + - ") are applicable to the specified index (" - + userProvidedIndexName + "). " + - "Please provide only one of them in the query expression."); - } else { - // found an applicable hash key condition - hashKeyNameForThisQuery = hashKeyName; - } - } - } - } - - // Collate all the key conditions - Map keyConditions = new HashMap(); - - // With user-provided index name - if (userProvidedIndexName != null) { - if (hasRangeKeyCondition - && (!userProvidedLsi) - && (!userProvidedGsi)) { - throw new IllegalArgumentException( - "Illegal query expression: No range key condition is applicable to the specified index (" - + userProvidedIndexName + "). "); - } - if (hashKeyNameForThisQuery == null) { - throw new IllegalArgumentException( - "Illegal query expression: No hash key condition is applicable to the specified index (" - + userProvidedIndexName + "). "); - } - - keyConditions.put(hashKeyNameForThisQuery, hashKeyConditions.get(hashKeyNameForThisQuery)); - if (hasRangeKeyCondition) { - keyConditions.putAll(rangeKeyConditions); - } - } else { - // Infer the index name by finding the index shared by both hash and range key annotations. - if (hasRangeKeyCondition) { - String inferredIndexName = null; - hashKeyNameForThisQuery = null; - if (hasPrimaryHashKeyCondition && hasPrimaryRangeKeyCondition) { - // Found valid query: primary hash + range key conditions - hashKeyNameForThisQuery = primaryHashKeyName; - } else { - // Intersect the set of all the indexes applicable to the range key - // with the set of indexes applicable to each hash key condition. - for (Map.Entry> indexedHashKeys : annotatedGsisOnHashKeys.entrySet()) { - String hashKeyName = indexedHashKeys.getKey(); - Set annotatedGsisOnHashKey = indexedHashKeys.getValue(); - - boolean foundValidQueryExpressionWithInferredIndex = false; - String indexNameInferredByThisHashKey = null; - if (hashKeyName.equals(primaryHashKeyName)) { - if (annotatedLsisOnRangeKey.size() == 1) { - // Found valid query (Primary hash + LSI range conditions) - foundValidQueryExpressionWithInferredIndex = true; - indexNameInferredByThisHashKey = annotatedLsisOnRangeKey.iterator().next(); - } - } - - // We don't need the data in annotatedGSIsOnHashKeys afterwards, - // so it's safe to do the intersection in-place. - annotatedGsisOnHashKey.retainAll(annotatedGsisOnRangeKey); - if (annotatedGsisOnHashKey.size() == 1) { - // Found valid query (Hash + range conditions on a GSI) - if (foundValidQueryExpressionWithInferredIndex) { - hashKeyNameForThisQuery = hashKeyName; - inferredIndexName = indexNameInferredByThisHashKey; - } - - foundValidQueryExpressionWithInferredIndex = true; - indexNameInferredByThisHashKey = annotatedGsisOnHashKey.iterator().next(); - } - - if (foundValidQueryExpressionWithInferredIndex) { - if (hashKeyNameForThisQuery != null) { - throw new IllegalArgumentException( - "Ambiguous query expression: Found multiple valid queries: " + - "(Hash: \"" + hashKeyNameForThisQuery + "\", Range: \"" + rangeKeyNameForThisQuery + - "\", Index: \"" + inferredIndexName + "\") and " + - "(Hash: \"" + hashKeyName + "\", Range: \"" + rangeKeyNameForThisQuery + - "\", Index: \"" + indexNameInferredByThisHashKey + "\")."); - } else { - hashKeyNameForThisQuery = hashKeyName; - inferredIndexName = indexNameInferredByThisHashKey; - } - } - } - } - - if (hashKeyNameForThisQuery != null) { - keyConditions.put(hashKeyNameForThisQuery, hashKeyConditions.get(hashKeyNameForThisQuery)); - keyConditions.putAll(rangeKeyConditions); - queryRequest = queryRequest.toBuilder().indexName(inferredIndexName).build(); - } else { - throw new IllegalArgumentException( - "Illegal query expression: Cannot infer the index name from the query expression."); - } - - } else { - // No range key condition is specified. - if (hashKeyConditions.size() > 1) { - if (hasPrimaryHashKeyCondition) { - keyConditions.put(primaryHashKeyName, hashKeyConditions.get(primaryHashKeyName)); - } else { - throw new IllegalArgumentException( - "Ambiguous query expression: More than one index hash key EQ conditions (" + - hashKeyConditions.keySet() + ") are applicable to the query. Please provide only one of them " + - "in the query expression, or specify the appropriate index name."); - } - - } else { - // Only one hash key condition - Entry> entry = annotatedGsisOnHashKeys.entrySet().iterator().next(); - String hashKeyName = entry.getKey(); - Set annotatedGsisOnHashkey = entry.getValue(); - if (!hasPrimaryHashKeyCondition) { - if (annotatedGsisOnHashkey.size() == 1) { - // Set the index if the index hash key is only annotated with one GSI. - queryRequest = queryRequest.toBuilder().indexName(annotatedGsisOnHashkey.iterator().next()).build(); - } else if (annotatedGsisOnHashkey.size() > 1) { - throw new IllegalArgumentException( - "Ambiguous query expression: More than one GSIs (" + - annotatedGsisOnHashkey + - ") are applicable to the query. " + - "Please specify one of them in your query expression."); - } else { - throw new IllegalArgumentException( - "Illegal query expression: No GSI is found in the @DynamoDBIndexHashKey annotation for " + - "attribute \"" + hashKeyName + "\"."); - } - } - keyConditions.putAll(hashKeyConditions); - } - - } - } - - return queryRequest.toBuilder().keyConditions(keyConditions).build(); - } - - /** - * Returns a new map object that merges the two sets of expected value - * conditions (user-specified or imposed by the internal implementation of - * DynamoDBMapper). Internal assertion on an attribute will be overridden by - * any user-specified condition on the same attribute. - *

    - * Exception is thrown if the two sets of conditions cannot be combined - * together. - */ - private static Map mergeExpectedAttributeValueConditions( - Map internalAssertions, - Map userProvidedConditions, - String userProvidedConditionOperator) { - // If any of the condition map is null, simply return a copy of the other one. - if ((internalAssertions == null || internalAssertions.isEmpty()) - && (userProvidedConditions == null || userProvidedConditions.isEmpty())) { - return null; - } else if (internalAssertions == null) { - return new HashMap<>(userProvidedConditions); - } else if (userProvidedConditions == null) { - return new HashMap<>(internalAssertions); - } - - // Start from a copy of the internal conditions - Map mergedExpectedValues = - new HashMap(internalAssertions); - - // Remove internal conditions that are going to be overlaid by user-provided ones. - for (String attrName : userProvidedConditions.keySet()) { - mergedExpectedValues.remove(attrName); - } - - // All the generated internal conditions must be joined by AND. - // Throw an exception if the user specifies an OR operator, and that the - // internal conditions are not totally overlaid by the user-provided - // ones. - if (ConditionalOperator.OR.toString().equals(userProvidedConditionOperator) - && !mergedExpectedValues.isEmpty()) { - throw new IllegalArgumentException("Unable to assert the value of the fields " + mergedExpectedValues.keySet() + - ", since the expected value conditions cannot be combined with user-specified " + - "conditions joined by \"OR\". You can use SaveBehavior.CLOBBER to " + - "skip the assertion on these fields."); - } - - mergedExpectedValues.putAll(userProvidedConditions); - - return mergedExpectedValues; - } - - static X applyUserAgent(X request) { - final AwsRequestOverrideConfiguration newCfg = request.overrideConfiguration() - .map(c -> c.toBuilder()) - .orElse(AwsRequestOverrideConfiguration.builder()) - .addApiName(apiName -> apiName.name(USER_AGENT_NAME).version(VersionInfo.SDK_VERSION)) - .build(); - - return (X) request.toBuilder() - .overrideConfiguration(newCfg) - .build(); - } - - static X applyBatchOperationUserAgent(X request) { - final AwsRequestOverrideConfiguration newCfg = request.overrideConfiguration() - .map(c -> c.toBuilder()) - .orElse(AwsRequestOverrideConfiguration.builder()) - .addApiName(apiName -> apiName.name(USER_AGENT_BATCH_OPERATION_NAME).version(VersionInfo.SDK_VERSION)) - .build(); - - return (X) request.toBuilder() - .overrideConfiguration(newCfg) - .build(); - } - - /** - * Batch pause. - */ - private static void pause(long delay) { - if (delay <= 0) { - return; - } - try { - Thread.sleep(delay); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw SdkClientException.builder().message(e.getMessage()).cause(e).build(); - } - } - - @Override - public DynamoDbMapperTableModel getTableModel(Class clazz, DynamoDbMapperConfig config) { - return this.models.getTableFactory(config).getTable(clazz); - } - - @Override - public T load(T keyObject, DynamoDbMapperConfig config) { - @SuppressWarnings("unchecked") - Class clazz = (Class) keyObject.getClass(); - - config = mergeConfig(config); - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - - String tableName = getTableName(clazz, keyObject, config); - - GetItemRequest.Builder rqBuilder = GetItemRequest.builder(); - - Map key = model.convertKey(keyObject); - - rqBuilder.key(key); - rqBuilder.tableName(tableName); - rqBuilder.consistentRead(config.getConsistentRead() == ConsistentRead.CONSISTENT); - - GetItemRequest rq = rqBuilder.build(); - - GetItemResponse item = db.getItem(applyUserAgent(rq)); - Map itemAttributes = item.item(); - if (itemAttributes == null) { - return null; - } - - T object = privateMarshallIntoObject( - toParameters(itemAttributes, clazz, tableName, config)); - - return object; - } - - @Override - public T load(Class clazz, Object hashKey, Object rangeKey, DynamoDbMapperConfig config) { - config = mergeConfig(config); - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - T keyObject = model.createKey(hashKey, rangeKey); - return load(keyObject, config); - } - - @Override - public T marshallIntoObject(Class clazz, Map itemAttributes, DynamoDbMapperConfig config) { - config = mergeConfig(config); - - String tableName = getTableName(clazz, config); - - return privateMarshallIntoObject( - toParameters(itemAttributes, clazz, tableName, config)); - } - - /** - * The one true implementation of marshallIntoObject. - */ - private T privateMarshallIntoObject( - AttributeTransformer.Parameters parameters) { - - Class clazz = parameters.modelClass(); - Map values = untransformAttributes(parameters); - - final DynamoDbMapperTableModel model = getTableModel(clazz, parameters.mapperConfig()); - return model.unconvert(values); - } - - @Override - public List marshallIntoObjects(Class clazz, List> itemAttributes, - DynamoDbMapperConfig config) { - // If config is used in the future, be sure to mergeConfig. - // config = mergeConfig(config); - - List result = new ArrayList(itemAttributes.size()); - for (Map item : itemAttributes) { - result.add(marshallIntoObject(clazz, item)); - } - return result; - } - - /** - * A replacement for {@link #marshallIntoObjects(Class, List)} that takes - * an extra set of parameters to be tunneled through to - * {@code privateMarshalIntoObject} (if nothing along the way is - * overridden). It's package-private because some of the Paginated*List - * classes call back into it, but final because no one, even in this - * package, should ever override it. - */ - final List marshallIntoObjects( - final List> parameters) { - List result = new ArrayList(parameters.size()); - - for (AttributeTransformer.Parameters entry : parameters) { - result.add(privateMarshallIntoObject(entry)); - } - - return result; - } - - @Override - public void save(T object, - DynamoDbSaveExpression saveExpression, - final DynamoDbMapperConfig config) { - final DynamoDbMapperConfig finalConfig = mergeConfig(config); - - @SuppressWarnings("unchecked") - Class clazz = (Class) object.getClass(); - String tableName = getTableName(clazz, object, finalConfig); - - final DynamoDbMapperTableModel model = getTableModel(clazz, finalConfig); - - /* - * We force a putItem request instead of updateItem request either when - * CLOBBER is configured, or part of the primary key of the object needs - * to be auto-generated. - */ - boolean forcePut = (finalConfig.saveBehavior() == SaveBehavior.CLOBBER) - || anyKeyGeneratable(model, object, finalConfig.saveBehavior()); - - SaveObjectHandler saveObjectHandler; - - if (forcePut) { - saveObjectHandler = this.new SaveObjectHandler(clazz, object, - tableName, finalConfig, saveExpression) { - - @Override - protected void onPrimaryKeyAttributeValue(String attributeName, - AttributeValue keyAttributeValue) { - /* Treat key values as common attribute value updates. */ - getAttributeValueUpdates().put(attributeName, - AttributeValueUpdate.builder() - .value(keyAttributeValue) - .action("PUT").build()); - } - - /* Use default implementation of onNonKeyAttribute(...) */ - - @Override - protected void onNullNonKeyAttribute(String attributeName) { - /* When doing a force put, we can safely ignore the null-valued attributes. */ - return; - } - - @Override - protected void executeLowLevelRequest() { - /* Send a putItem request. */ - doPutItem(); - } - }; - } else { - saveObjectHandler = this.new SaveObjectHandler(clazz, object, - tableName, finalConfig, saveExpression) { - - @Override - protected void onPrimaryKeyAttributeValue(String attributeName, - AttributeValue keyAttributeValue) { - /* Put it in the key collection which is later used in the updateItem request. */ - getPrimaryKeyAttributeValues().put(attributeName, keyAttributeValue); - } - - - @Override - protected void onNonKeyAttribute(String attributeName, - AttributeValue currentValue) { - /* If it's a set attribute and the mapper is configured with APPEND_SET, - * we do an "ADD" update instead of the default "PUT". - */ - if (localSaveBehavior() == SaveBehavior.APPEND_SET) { - if (currentValue.bs() != null - || currentValue.ns() != null - || currentValue.ss() != null) { - getAttributeValueUpdates().put( - attributeName, - AttributeValueUpdate.builder().value( - currentValue).action("ADD").build()); - return; - } - } - /* Otherwise, we do the default "PUT" update. */ - super.onNonKeyAttribute(attributeName, currentValue); - } - - @Override - protected void onNullNonKeyAttribute(String attributeName) { - /* - * If UPDATE_SKIP_NULL_ATTRIBUTES or APPEND_SET is - * configured, we don't delete null value attributes. - */ - if (localSaveBehavior() == SaveBehavior.UPDATE_SKIP_NULL_ATTRIBUTES - || localSaveBehavior() == SaveBehavior.APPEND_SET) { - return; - } else { - /* Delete attributes that are set as null in the object. */ - getAttributeValueUpdates() - .put(attributeName, - AttributeValueUpdate.builder() - .action("DELETE") - .build()); - } - } - - @Override - protected void executeLowLevelRequest() { - UpdateItemResponse updateItemResult = doUpdateItem(); - - // The UpdateItem request is specified to return ALL_NEW - // attributes of the affected item. So if the returned - // UpdateItemResponse does not include any ReturnedAttributes, - // it indicates the UpdateItem failed silently (e.g. the - // key-only-put nightmare - - // https://forums.aws.amazon.com/thread.jspa?threadID=86798&tstart=25), - // in which case we should re-send a PutItem - // request instead. - if (updateItemResult.attributes() == null - || updateItemResult.attributes().isEmpty()) { - // Before we proceed with PutItem, we need to put all - // the key attributes (prepared for the - // UpdateItemRequest) into the AttributeValueUpdates - // collection. - for (String keyAttributeName : getPrimaryKeyAttributeValues().keySet()) { - AttributeValueUpdate value = AttributeValueUpdate.builder() - .value(getPrimaryKeyAttributeValues().get(keyAttributeName)) - .action("PUT").build(); - getAttributeValueUpdates().put(keyAttributeName, value); - } - - doPutItem(); - } - } - }; - } - - saveObjectHandler.execute(); - } - - @Override - public void delete(T object, DynamoDbDeleteExpression deleteExpression, DynamoDbMapperConfig config) { - config = mergeConfig(config); - - @SuppressWarnings("unchecked") - Class clazz = (Class) object.getClass(); - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - - String tableName = getTableName(clazz, object, config); - - Map key = model.convertKey(object); - - /* - * If there is a version field, make sure we assert its value. If the - * version field is null (only should happen in unusual circumstances), - * pretend it doesn't have a version field after all. - */ - Map internalAssertions = new HashMap(); - if (config.saveBehavior() != SaveBehavior.CLOBBER && model.versioned()) { - for (final DynamoDbMapperFieldModel field : model.versions()) { - final AttributeValue current = field.getAndConvert(object); - if (current == null) { - internalAssertions.put(field.name(), ExpectedAttributeValue.builder().exists(false).build()); - } else { - internalAssertions.put(field.name(), ExpectedAttributeValue.builder().exists(true).value(current).build()); - } - break; - } - } - - DeleteItemRequest req = DeleteItemRequest.builder() - .key(key) - .tableName(tableName) - .expected(internalAssertions) - .build(); - - if (deleteExpression != null) { - String conditionalExpression = deleteExpression.getConditionExpression(); - - if (conditionalExpression != null) { - if (!internalAssertions.isEmpty()) { - throw SdkClientException.builder() - .message("Condition Expressions cannot be used if a versioned attribute is present") - .build(); - } - - req = req.toBuilder() - .conditionExpression(conditionalExpression) - .expressionAttributeNames( - deleteExpression.getExpressionAttributeNames()) - .expressionAttributeValues( - deleteExpression.getExpressionAttributeValues()) - .build(); - } - - req = req.toBuilder() - .expected( - mergeExpectedAttributeValueConditions(internalAssertions, - deleteExpression.getExpected(), - deleteExpression.getConditionalOperator())) - .conditionalOperator( - deleteExpression.getConditionalOperator()) - .build(); - - } - db.deleteItem(applyUserAgent(req)); - } - - @Override - public List batchWrite(Iterable objectsToWrite, - Iterable objectsToDelete, - DynamoDbMapperConfig config) { - config = mergeConfig(config); - - List totalFailedBatches = new LinkedList(); - - StringListMap requestItems = new StringListMap(); - - List inMemoryUpdates = new LinkedList(); - for (Object toWrite : objectsToWrite) { - Class clazz = (Class) toWrite.getClass(); - String tableName = getTableName(clazz, toWrite, config); - - Map attributeValues = new HashMap(); - - // Look at every getter and construct a value object for it - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - for (final DynamoDbMapperFieldModel field : model.fields()) { - AttributeValue currentValue; - if (canGenerate(model, toWrite, config.saveBehavior(), field) && !field.versioned()) { - currentValue = field.convert(field.generate(field.get(toWrite))); - inMemoryUpdates.add(new ValueUpdate(field, currentValue, toWrite)); - } else { - currentValue = field.convert(field.get(toWrite)); - } - if (currentValue != null) { - attributeValues.put(field.name(), currentValue); - } - } - - if (!requestItems.containsKey(tableName)) { - requestItems.put(tableName, new LinkedList()); - } - - AttributeTransformer.Parameters parameters = - toParameters(attributeValues, clazz, tableName, config); - - requestItems.add(tableName, - WriteRequest.builder() - .putRequest(PutRequest.builder() - .item(transformAttributes(parameters)) - .build()) - .build()); - } - - for (Object toDelete : objectsToDelete) { - Class clazz = (Class) toDelete.getClass(); - - String tableName = getTableName(clazz, toDelete, config); - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - - Map key = model.convertKey(toDelete); - - requestItems.add(tableName, WriteRequest.builder() - .deleteRequest(DeleteRequest.builder() - .key(key) - .build()) - .build()); - } - - // Break into chunks of 25 items and make service requests to DynamoDB - for (final StringListMap batch : requestItems.subMaps(MAX_ITEMS_PER_BATCH, true)) { - List failedBatches = writeOneBatch(batch, config.batchWriteRetryStrategy()); - totalFailedBatches.addAll(failedBatches); - - // If contains throttling exception, we do a backoff - if (containsThrottlingException(failedBatches)) { - pause(config.batchWriteRetryStrategy().getDelayBeforeRetryUnprocessedItems( - Collections.unmodifiableMap(batch), 0)); - } - } - - // Once the entire batch is processed, update assigned keys in memory - for (ValueUpdate update : inMemoryUpdates) { - update.apply(); - } - - return totalFailedBatches; - } - - /** - * Process one batch of requests(max 25). It will divide the batch if - * receives request too large exception(the total size of the request is beyond 1M). - */ - private List writeOneBatch( - StringListMap batch, - BatchWriteRetryStrategy batchWriteRetryStrategy) { - - List failedBatches = new LinkedList(); - FailedBatch failedBatch = doBatchWriteItemWithRetry(batch, batchWriteRetryStrategy); - - if (failedBatch != null) { - // If the exception is request entity too large, we divide the batch - // into smaller parts. - - if (failedBatch.isRequestEntityTooLarge()) { - - // If only one item left, the item size must beyond 64k, which - // exceedes the limit. - - if (failedBatch.size() == 1) { - failedBatches.add(failedBatch); - } else { - for (final StringListMap subBatch : batch.subMaps(2, false)) { - failedBatches.addAll(writeOneBatch(subBatch, batchWriteRetryStrategy)); - } - } - - } else { - failedBatches.add(failedBatch); - } - - } - return failedBatches; - } - - /** - * Check whether there are throttling exception in the failed batches. - */ - private boolean containsThrottlingException(List failedBatches) { - for (FailedBatch failedBatch : failedBatches) { - if (failedBatch.isThrottling()) { - return true; - } - } - return false; - } - - /** - * Continue trying to process the batch and retry on UnproccessedItems as - * according to the specified BatchWriteRetryStrategy - */ - private FailedBatch doBatchWriteItemWithRetry( - Map> batch, - BatchWriteRetryStrategy batchWriteRetryStrategy) { - - BatchWriteItemResponse result = null; - int retries = 0; - int maxRetries = batchWriteRetryStrategy - .maxRetryOnUnprocessedItems(Collections - .unmodifiableMap(batch)); - - FailedBatch failedBatch = null; - Map> pendingItems = batch; - - while (true) { - try { - result = db.batchWriteItem(applyBatchOperationUserAgent( - BatchWriteItemRequest.builder().requestItems(pendingItems).build())); - } catch (Exception e) { - failedBatch = new FailedBatch(); - failedBatch.setUnprocessedItems(pendingItems); - failedBatch.setException(e); - return failedBatch; - } - pendingItems = result.unprocessedItems(); - - if (pendingItems.size() > 0) { - - // return pendingItems as a FailedBatch if we have exceeded max retry - if (maxRetries >= 0 && retries >= maxRetries) { - failedBatch = new FailedBatch(); - failedBatch.setUnprocessedItems(pendingItems); - failedBatch.setException(null); - return failedBatch; - } - - pause(batchWriteRetryStrategy.getDelayBeforeRetryUnprocessedItems( - Collections.unmodifiableMap(pendingItems), retries)); - retries++; - } else { - break; - } - } - return failedBatch; - } - - @Override - public Map> batchLoad(Iterable itemsToGet, DynamoDbMapperConfig config) { - config = mergeConfig(config); - boolean consistentReads = (config.getConsistentRead() == ConsistentRead.CONSISTENT); - - if (itemsToGet == null) { - return new HashMap<>(); - } - - Map>> requestItemLists = new HashMap<>(); - Map> classesByTableName = new HashMap>(); - Map> resultSet = new HashMap>(); - int count = 0; - - for (Object keyObject : itemsToGet) { - Class clazz = (Class) keyObject.getClass(); - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - - String tableName = getTableName(clazz, keyObject, config); - classesByTableName.put(tableName, clazz); - - requestItemLists.computeIfAbsent(tableName, ignored -> new LinkedList<>()).add(model.convertKey(keyObject)); - - // Reach the maximum number which can be handled in a single batchGet - if (++count == 100) { - Map requestItems = batchRequestItems(consistentReads, requestItemLists); - processBatchGetRequest(classesByTableName, requestItems, resultSet, config); - requestItemLists.clear(); - count = 0; - } - } - - if (count > 0) { - Map requestItems = batchRequestItems(consistentReads, requestItemLists); - processBatchGetRequest(classesByTableName, requestItems, resultSet, config); - } - - return resultSet; - } - - private Map batchRequestItems( - boolean consistentReads, - Map>> requestItemLists) { - return requestItemLists.entrySet().stream() - .collect(toMap(Entry::getKey, e -> KeysAndAttributes.builder() - .consistentRead(consistentReads) - .keys(e.getValue()) - .build())); - } - - @Override - public Map> batchLoad(Map, List> itemsToGet, DynamoDbMapperConfig config) { - config = mergeConfig(config); - List keys = new ArrayList(); - if (itemsToGet != null) { - for (Map.Entry, List> item : itemsToGet.entrySet()) { - Class clazz = item.getKey(); - List value = item.getValue(); - if (value != null) { - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - for (KeyPair keyPair : value) { - keys.add(model.createKey(keyPair.getHashKey(), keyPair.getRangeKey())); - } - } - } - } - return batchLoad(keys, config); - } - - /** - * @param config never null - */ - private void processBatchGetRequest( - final Map> classesByTableName, - final Map requestItems, - final Map> resultSet, - final DynamoDbMapperConfig config) { - - BatchGetItemResponse batchGetItemResponse = null; - BatchGetItemRequest batchGetItemRequest = BatchGetItemRequest.builder() - .requestItems(requestItems) - .build(); - - BatchLoadRetryStrategy batchLoadStrategy = config.batchLoadRetryStrategy(); - - BatchLoadContext batchLoadContext = new BatchLoadContext(batchGetItemRequest); - - int retries = 0; - - do { - if (batchGetItemResponse != null) { - retries++; - batchLoadContext.setRetriesAttempted(retries); - if (!isNullOrEmpty(batchGetItemResponse.unprocessedKeys())) { - pause(batchLoadStrategy.getDelayBeforeNextRetry(batchLoadContext)); - batchGetItemRequest = batchGetItemRequest.toBuilder() - .requestItems(batchGetItemResponse.unprocessedKeys()) - .build(); - batchLoadContext.setBatchGetItemRequest(batchGetItemRequest); - } - } - - batchGetItemResponse = db.batchGetItem(applyBatchOperationUserAgent(batchGetItemRequest)); - - Map>> responses = batchGetItemResponse.responses(); - for (Map.Entry>> entries : responses.entrySet()) { - String tableName = entries.getKey(); - List> items = entries.getValue(); - - List objects = resultSet.getOrDefault(tableName, new LinkedList<>()); - Class clazz = classesByTableName.get(tableName); - - for (Map item : items) { - AttributeTransformer.Parameters parameters = toParameters(item, clazz, tableName, config); - objects.add(privateMarshallIntoObject(parameters)); - } - - resultSet.put(tableName, objects); - } - - batchLoadContext.setBatchGetItemResponse(batchGetItemResponse); - - // the number of unprocessed keys and Batch Load Strategy will drive the number of retries - } while (batchLoadStrategy.shouldRetry(batchLoadContext)); - - if (!isNullOrEmpty(batchGetItemResponse.unprocessedKeys())) { - throw new BatchGetItemException("The BatchGetItemResponse has unprocessed keys after max retry attempts. Catch the " + - "BatchGetItemException to get the list of unprocessed keys.", - batchGetItemResponse.unprocessedKeys(), resultSet); - } - } - - @Override - public PaginatedScanList scan(Class clazz, - DynamoDbScanExpression scanExpression, - DynamoDbMapperConfig config) { - config = mergeConfig(config); - - ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config); - - ScanResponse scanResult = db.scan(applyUserAgent(scanRequest)); - return new PaginatedScanList<>(this, clazz, db, scanRequest, scanResult, config.getPaginationLoadingStrategy(), config); - } - - @Override - public PaginatedParallelScanList parallelScan(Class clazz, - DynamoDbScanExpression scanExpression, - int totalSegments, - DynamoDbMapperConfig config) { - config = mergeConfig(config); - - // Create hard copies of the original scan request with difference segment number. - List parallelScanRequests = createParallelScanRequestsFromExpression(clazz, scanExpression, - totalSegments, config); - ParallelScanTask parallelScanTask = new ParallelScanTask(db, parallelScanRequests); - - return new PaginatedParallelScanList(this, clazz, db, parallelScanTask, config.getPaginationLoadingStrategy(), config); - } - - @Override - public ScanResultPage scanPage(Class clazz, - DynamoDbScanExpression scanExpression, - DynamoDbMapperConfig config) { - config = mergeConfig(config); - - ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config); - - ScanResponse scanResult = db.scan(applyUserAgent(scanRequest)); - ScanResultPage result = new ScanResultPage(); - List> parameters = - toParameters(scanResult.items(), clazz, scanRequest.tableName(), config); - - result.setResults(marshallIntoObjects(parameters)); - result.setLastEvaluatedKey(scanResult.lastEvaluatedKey()); - result.setCount(scanResult.count()); - result.setScannedCount(scanResult.scannedCount()); - result.setConsumedCapacity(scanResult.consumedCapacity()); - - return result; - } - - @Override - public PaginatedQueryList query(Class clazz, - DynamoDbQueryExpression queryExpression, - DynamoDbMapperConfig config) { - config = mergeConfig(config); - - QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config); - - QueryResponse queryResult = db.query(applyUserAgent(queryRequest)); - return new PaginatedQueryList(this, clazz, db, queryRequest, queryResult, - config.getPaginationLoadingStrategy(), config); - } - - @Override - public QueryResultPage queryPage(Class clazz, - DynamoDbQueryExpression queryExpression, - DynamoDbMapperConfig config) { - config = mergeConfig(config); - - QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config); - - QueryResponse queryResult = db.query(applyUserAgent(queryRequest)); - QueryResultPage result = new QueryResultPage(); - List> parameters = - toParameters(queryResult.items(), clazz, queryRequest.tableName(), config); - - result.setResults(marshallIntoObjects(parameters)); - result.setLastEvaluatedKey(queryResult.lastEvaluatedKey()); - result.setCount(queryResult.count()); - result.setScannedCount(queryResult.scannedCount()); - result.setConsumedCapacity(queryResult.consumedCapacity()); - - return result; - } - - @Override - public int count(Class clazz, DynamoDbScanExpression scanExpression, DynamoDbMapperConfig config) { - config = mergeConfig(config); - - ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config); - scanRequest = scanRequest.toBuilder().select(Select.COUNT).build(); - - // Count scans can also be truncated for large datasets - int count = 0; - ScanResponse scanResult; - do { - scanResult = db.scan(applyUserAgent(scanRequest)); - count += scanResult.count(); - scanRequest = scanRequest.toBuilder().exclusiveStartKey(scanResult.lastEvaluatedKey()).build(); - } while (!(scanResult.lastEvaluatedKey() instanceof SdkAutoConstructMap)); - - return count; - } - - @Override - public int count(Class clazz, DynamoDbQueryExpression queryExpression, DynamoDbMapperConfig config) { - config = mergeConfig(config); - - QueryRequest queryRequest = createQueryRequestFromExpression(clazz, queryExpression, config); - queryRequest = queryRequest.toBuilder().select(Select.COUNT).build(); - - // Count queries can also be truncated for large datasets - int count = 0; - QueryResponse queryResult; - do { - queryResult = db.query(applyUserAgent(queryRequest)); - count += queryResult.count(); - queryRequest = queryRequest.toBuilder().exclusiveStartKey(queryResult.lastEvaluatedKey()).build(); - } while (!(queryResult.lastEvaluatedKey() instanceof SdkAutoConstructMap)); - - return count; - } - - /** - * @param config never null - */ - private ScanRequest createScanRequestFromExpression(Class clazz, DynamoDbScanExpression scanExpression, - DynamoDbMapperConfig config) { - ScanRequest scanRequest = ScanRequest.builder() - .tableName(getTableName(clazz, config)) - .indexName(scanExpression.getIndexName()) - .scanFilter(scanExpression.scanFilter()) - .limit(scanExpression.limit()) - .exclusiveStartKey(scanExpression.getExclusiveStartKey()) - .totalSegments(scanExpression.getTotalSegments()) - .segment(scanExpression.segment()) - .conditionalOperator(scanExpression.getConditionalOperator()) - .filterExpression(scanExpression.getFilterExpression()) - .expressionAttributeNames(scanExpression.getExpressionAttributeNames()) - .expressionAttributeValues(scanExpression.getExpressionAttributeValues()) - .select(scanExpression.select()) - .projectionExpression(scanExpression.getProjectionExpression()) - .returnConsumedCapacity(scanExpression.getReturnConsumedCapacity()) - .consistentRead(scanExpression.isConsistentRead()) - .build(); - - return applyUserAgent(scanRequest); - } - - /** - * @param config never null - */ - private List createParallelScanRequestsFromExpression(Class clazz, DynamoDbScanExpression scanExpression, - int totalSegments, DynamoDbMapperConfig config) { - if (totalSegments < 1) { - throw new IllegalArgumentException("Parallel scan should have at least one scan segment."); - } - if (scanExpression.getExclusiveStartKey() != null) { - log.info("The ExclusiveStartKey parameter specified in the DynamoDBScanExpression is ignored," - + " since the individual parallel scan request on each segment is applied on a separate key scope."); - } - if (scanExpression.segment() != null || scanExpression.getTotalSegments() != null) { - log.info("The Segment and TotalSegments parameters specified in the DynamoDBScanExpression are ignored."); - } - - List parallelScanRequests = new LinkedList(); - for (int segment = 0; segment < totalSegments; segment++) { - ScanRequest scanRequest = createScanRequestFromExpression(clazz, scanExpression, config) - .toBuilder() - .segment(segment) - .totalSegments(totalSegments) - .exclusiveStartKey(null) - .build(); - parallelScanRequests.add(scanRequest); - } - return parallelScanRequests; - } - - private QueryRequest createQueryRequestFromExpression(Class clazz, - DynamoDbQueryExpression xpress, DynamoDbMapperConfig config) { - - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - - QueryRequest request = QueryRequest.builder() - .consistentRead(xpress.isConsistentRead()) - .tableName(getTableName(clazz, xpress.getHashKeyValues(), config)) - .indexName(xpress.getIndexName()) - .keyConditionExpression(xpress.getKeyConditionExpression()) - .build(); - - request = processKeyConditions(request, xpress, model); - - request = request.toBuilder() - .scanIndexForward(xpress.isScanIndexForward()) - .limit(xpress.limit()) - .exclusiveStartKey(xpress.getExclusiveStartKey()) - .queryFilter(xpress.getQueryFilter()) - .conditionalOperator(xpress.getConditionalOperator()) - .select(xpress.select()) - .projectionExpression(xpress.getProjectionExpression()) - .filterExpression(xpress.getFilterExpression()) - .expressionAttributeNames(xpress.getExpressionAttributeNames()) - .expressionAttributeValues(xpress.getExpressionAttributeValues()) - .returnConsumedCapacity(xpress.getReturnConsumedCapacity()) - .build(); - - return applyUserAgent(request); - } - - private AttributeTransformer.Parameters toParameters( - final Map attributeValues, - final Class modelClass, - final String tableName, - final DynamoDbMapperConfig mapperConfig) { - - return toParameters(attributeValues, false, modelClass, tableName, mapperConfig); - } - - private AttributeTransformer.Parameters toParameters( - final Map attributeValues, - final boolean partialUpdate, - final Class modelClass, - final String tableName, - final DynamoDbMapperConfig mapperConfig) { - - return new TransformerParameters( - getTableModel(modelClass, mapperConfig), - attributeValues, - partialUpdate, - modelClass, - mapperConfig, - tableName); - } - - final List> toParameters( - final List> attributeValues, - final Class modelClass, - final String tableName, - final DynamoDbMapperConfig mapperConfig) { - List> rval = - new ArrayList>( - attributeValues.size()); - - for (Map item : attributeValues) { - rval.add(toParameters(item, modelClass, tableName, mapperConfig)); - } - - return rval; - } - - private Map untransformAttributes( - final AttributeTransformer.Parameters parameters) { - if (transformer != null) { - return transformer.untransform(parameters); - } else { - return parameters.getAttributeValues(); - } - } - - private Map transformAttributes( - final AttributeTransformer.Parameters parameters) { - - if (transformer != null) { - return transformer.transform(parameters); - } else { - return parameters.getAttributeValues(); - } - } - - @Override - public S3ClientCache s3ClientCache() { - return s3Links.s3ClientCache(); - } - - @Override - public S3Link createS3Link(Region s3region, String bucketName, String key) { - return s3Links.createS3Link(s3region, bucketName, key); - } - - @Override - public S3Link createS3Link(String s3region, String bucketName, String key) { - return s3Links.createS3Link(s3region, bucketName, key); - } - - @Override - public CreateTableRequest generateCreateTableRequest(Class clazz, DynamoDbMapperConfig config) { - config = mergeConfig(config); - final DynamoDbMapperTableModel model = getTableModel(clazz, config); - - List keySchemas = new ArrayList<>(); - keySchemas.add(KeySchemaElement.builder().attributeName(model.hashKey().name()).keyType(HASH).build()); - - final CreateTableRequest.Builder requestBuilder = CreateTableRequest.builder() - .tableName(getTableName(clazz, config)); - - if (model.rangeKeyIfExists() != null) { - keySchemas.add(KeySchemaElement.builder() - .attributeName(model.rangeKey().name()) - .keyType(RANGE) - .build()); - } - requestBuilder.globalSecondaryIndexes(model.globalSecondaryIndexes()) - .localSecondaryIndexes(model.localSecondaryIndexes()); - - List attributeDefinitions = new ArrayList<>(); - for (final DynamoDbMapperFieldModel field : model.fields()) { - if (field.keyType() != null || field.indexed()) { - AttributeDefinition attributeDefinition = AttributeDefinition.builder() - .attributeType(ScalarAttributeType.valueOf(field.attributeType().name())) - .attributeName(field.name()) - .build(); - - attributeDefinitions.add(attributeDefinition); - } - } - - requestBuilder.keySchema(keySchemas); - requestBuilder.attributeDefinitions(attributeDefinitions); - return requestBuilder.build(); - } - - @Override - public DeleteTableRequest generateDeleteTableRequest(Class clazz, DynamoDbMapperConfig config) { - config = mergeConfig(config); - DeleteTableRequest deleteTableRequest = DeleteTableRequest.builder() - .tableName(getTableName(clazz, config)) - .build(); - return deleteTableRequest; - } - - /** - * Creates a new table mapper using this mapper to perform operations. - * @param The object type which this mapper operates. - * @param The hash key value type. - * @param The range key value type; use ? if no range key. - * @param clazz The object class. - * @return The table mapper. - */ - public DynamoDbTableMapper newTableMapper(Class clazz) { - DynamoDbMapperConfig config = mergeConfig(null); - return new DynamoDbTableMapper(this.db, this, config, getTableModel(clazz, config)); - } - - /** - * The one true implementation of AttributeTransformer.Parameters. - */ - private static class TransformerParameters - implements AttributeTransformer.Parameters { - - private final DynamoDbMapperTableModel model; - private final Map attributeValues; - private final boolean partialUpdate; - private final Class modelClass; - private final DynamoDbMapperConfig mapperConfig; - private final String tableName; - - TransformerParameters( - final DynamoDbMapperTableModel model, - final Map attributeValues, - final boolean partialUpdate, - final Class modelClass, - final DynamoDbMapperConfig mapperConfig, - final String tableName) { - - this.model = model; - this.attributeValues = - Collections.unmodifiableMap(attributeValues); - this.partialUpdate = partialUpdate; - this.modelClass = modelClass; - this.mapperConfig = mapperConfig; - this.tableName = tableName; - } - - @Override - public Map getAttributeValues() { - return attributeValues; - } - - @Override - public boolean isPartialUpdate() { - return partialUpdate; - } - - @Override - public Class modelClass() { - return modelClass; - } - - @Override - public DynamoDbMapperConfig mapperConfig() { - return mapperConfig; - } - - @Override - public String getTableName() { - return tableName; - } - - @Override - public String getHashKeyName() { - return model.hashKey().name(); - } - - @Override - public String getRangeKeyName() { - return model.rangeKeyIfExists() == null ? null : model.rangeKey().name(); - } - } - - /** - * The return type of batchWrite, batchDelete and batchSave. - * - * It contains the information about the unprocessed items and the - * exception causing the failure. - */ - public static class FailedBatch { - private Map> unprocessedItems; - private Exception exception; - - public Map> getUnprocessedItems() { - return unprocessedItems; - } - - public void setUnprocessedItems(Map> unprocessedItems) { - this.unprocessedItems = unprocessedItems; - } - - public Exception getException() { - return exception; - } - - public void setException(Exception excetpion) { - this.exception = excetpion; - } - - private boolean isRequestEntityTooLarge() { - return exception instanceof SdkServiceException && - RetryUtils.isRequestEntityTooLargeException((SdkServiceException) exception); - } - - private boolean isThrottling() { - return exception instanceof SdkServiceException && ((SdkServiceException) exception).isThrottlingException(); - } - - private int size() { - int size = 0; - for (final List values : unprocessedItems.values()) { - size += values.size(); - } - return size; - } - } - - /** - * Used for batch operations where request data is grouped by table name. - */ - static final class StringListMap extends LinkedHashMap> { - private static final long serialVersionUID = -1L; - - public List getPutIfNotExists(final String key) { - List list = get(key); - if (list == null) { - list = new LinkedList<>(); - put(key, list); - } - return list; - } - - public boolean add(final String key, final T value) { - return getPutIfNotExists(key).add(value); - } - - public List> subMaps(final int size, boolean perMap) { - final LinkedList> maps = new LinkedList>(); - int index = 0; - int count = 0; - for (final Entry> entry : entrySet()) { - for (final T value : entry.getValue()) { - if (index == maps.size()) { - maps.add(new StringListMap()); - } - maps.get(index).add(entry.getKey(), value); - index = perMap ? (++count / size) : (++index % size); - } - } - return maps; - } - } - - public static final class BatchGetItemException extends SdkClientException { - private transient Map unprocessedKeys; - private transient Map> responses; - - public BatchGetItemException(String message, Map unprocessedKeys, - Map> responses) { - super(SdkClientException.builder().message(message)); - this.unprocessedKeys = unprocessedKeys; - this.responses = responses; - } - - /** - * Returns a map of tables and their respective keys that were not processed during the operation.. - */ - public Map getUnprocessedKeys() { - return unprocessedKeys; - } - - /** - * Returns a map of the loaded objects. Each key in the map is the name of a DynamoDB table. - * Each value in the map is a list of objects that have been loaded from that table. All - * objects for each table can be cast to the associated user defined type that is - * annotated as mapping that table. - */ - public Map> getResponses() { - return responses; - } - } - - /** - * The handler for saving object using DynamoDBMapper. Caller should - * implement the abstract methods to provide the expected behavior on each - * scenario, and this handler will take care of all the other basic workflow - * and common operations. - */ - protected abstract class SaveObjectHandler { - - protected final Object object; - protected final Class clazz; - /** - * Additional expected value conditions specified by the user. - */ - protected final Map userProvidedExpectedValueConditions; - /** - * Condition operator on the additional expected value conditions - * specified by the user. - */ - protected final String userProvidedConditionOperator; - private final String tableName; - private final DynamoDbMapperConfig saveConfig; - private final Map primaryKeys; - private final Map updateValues; - /** - * Any expected value conditions specified by the implementation of - * DynamoDBMapper, e.g. value assertions on versioned attributes. - */ - private final Map internalExpectedValueAssertions; - private final List inMemoryUpdates; - - /** - * Constructs a handler for saving the specified model object. - * - * @param object The model object to be saved. - * @param clazz The domain class of the object. - * @param tableName The table name. - * @param saveConfig The mapper configuration used for this save. - * @param saveExpression The save expression, including the user-provided conditions and an optional logic operator. - */ - public SaveObjectHandler( - Class clazz, - Object object, - String tableName, - DynamoDbMapperConfig saveConfig, - DynamoDbSaveExpression saveExpression) { - - this.clazz = clazz; - this.object = object; - this.tableName = tableName; - this.saveConfig = saveConfig; - - if (saveExpression != null) { - userProvidedExpectedValueConditions = saveExpression - .getExpected(); - userProvidedConditionOperator = saveExpression - .getConditionalOperator(); - } else { - userProvidedExpectedValueConditions = null; - userProvidedConditionOperator = null; - } - - updateValues = new HashMap<>(); - internalExpectedValueAssertions = new HashMap<>(); - inMemoryUpdates = new LinkedList<>(); - primaryKeys = new HashMap<>(); - } - - /** - * The general workflow of a save operation. - */ - public void execute() { - final DynamoDbMapperTableModel model = getTableModel((Class) clazz, saveConfig); - for (final DynamoDbMapperFieldModel field : model.fields()) { - if (canGenerate(model, object, localSaveBehavior(), field)) { - if (field.keyType() != null || field.indexed()) { - onAutoGenerateAssignableKey(field); - } else if (field.versioned()) { - onVersionAttribute(field); - } else { - onAutoGenerate(field); - } - } else if (field.keyType() != null) { - AttributeValue newAttributeValue = field.convert(field.get(object)); - if (newAttributeValue == null) { - throw new DynamoDbMappingException( - clazz.getSimpleName() + "[" + field.name() + "]; null or empty value for primary key" - ); - } - onPrimaryKeyAttributeValue(field.name(), newAttributeValue); - } else { - AttributeValue currentValue = field.convert(field.get(object)); - if (currentValue != null) { - onNonKeyAttribute(field.name(), currentValue); - } else { - onNullNonKeyAttribute(field.name()); - } - } - } - - /* - * Execute the implementation of the low level request. - */ - executeLowLevelRequest(); - - /* - * Finally, after the service call has succeeded, update the - * in-memory object with new field values as appropriate. This - * currently takes into account of auto-generated keys and versioned - * attributes. - */ - for (ValueUpdate update : inMemoryUpdates) { - update.apply(); - } - } - - /** - * Implement this method to do the necessary operations when a primary key - * attribute is set with some value. - * - * @param attributeName - * The name of the primary key attribute. - * @param keyAttributeValue - * The AttributeValue of the primary key attribute as specified in - * the object. - */ - protected abstract void onPrimaryKeyAttributeValue(String attributeName, AttributeValue keyAttributeValue); - - /** - * Implement this method for necessary operations when a non-key - * attribute is set a non-null value in the object. - * The default implementation simply adds a "PUT" update for the given attribute. - * - * @param attributeName - * The name of the non-key attribute. - * @param currentValue - * The updated value of the given attribute. - */ - protected void onNonKeyAttribute(String attributeName, AttributeValue currentValue) { - updateValues.put(attributeName, AttributeValueUpdate.builder() - .value(currentValue) - .action("PUT") - .build()); - } - - /** - * Implement this method for necessary operations when a non-key - * attribute is set null in the object. - * - * @param attributeName - * The name of the non-key attribute. - */ - protected abstract void onNullNonKeyAttribute(String attributeName); - - /** - * Implement this method to send the low-level request that is necessary - * to complete the save operation. - */ - protected abstract void executeLowLevelRequest(); - - /** Get the SaveBehavior used locally for this save operation. **/ - protected SaveBehavior localSaveBehavior() { - return saveConfig.saveBehavior(); - } - - /** Get the table name **/ - protected String getTableName() { - return tableName; - } - - /** Get the map of all the specified primamry keys of the saved object. **/ - protected Map getPrimaryKeyAttributeValues() { - return primaryKeys; - } - - /** Get the map of AttributeValueUpdate on each modeled attribute. **/ - protected Map getAttributeValueUpdates() { - return updateValues; - } - - /** - * Merge and return all the expected value conditions (either - * user-specified or imposed by the internal implementation of - * DynamoDBMapper) for this save operation. - */ - protected Map mergeExpectedAttributeValueConditions() { - return DynamoDbMapper.mergeExpectedAttributeValueConditions( - internalExpectedValueAssertions, - userProvidedExpectedValueConditions, - userProvidedConditionOperator); - } - - /** Get the list of all the necessary in-memory update on the object. **/ - protected List getInMemoryUpdates() { - return inMemoryUpdates; - } - - /** - * Save the item using a UpdateItem request. The handler will call this - * method if - *
      - *
    • CLOBBER configuration is not being used; - *
    • AND the item does not contain auto-generated key value; - *
    - *

    - * The ReturnedValues parameter for the UpdateItem request is set as - * ALL_NEW, which means the service should return all of the attributes - * of the new version of the item after the update. The handler will use - * the returned attributes to detect silent failure on the server-side. - */ - protected UpdateItemResponse doUpdateItem() { - UpdateItemRequest req = UpdateItemRequest.builder() - .tableName(getTableName()) - .key(getPrimaryKeyAttributeValues()) - .attributeUpdates( - transformAttributeUpdates( - this.clazz, - getTableName(), - getPrimaryKeyAttributeValues(), - getAttributeValueUpdates(), - saveConfig)) - .expected(mergeExpectedAttributeValueConditions()) - .conditionalOperator(userProvidedConditionOperator) - .returnValues(ReturnValue.ALL_NEW) - .build(); - - return db.updateItem(applyUserAgent(req)); - } - - /** - * Save the item using a PutItem request. The handler will call this - * method if - *

      - *
    • CLOBBER configuration is being used; - *
    • OR the item contains auto-generated key value; - *
    • OR an UpdateItem request has silently failed (200 response with - * no affected attribute), which indicates the key-only-put scenario - * that we used to handle by the keyOnlyPut(...) hack. - *
    - */ - protected PutItemResponse doPutItem() { - Map attributeValues = convertToItem(getAttributeValueUpdates()); - - attributeValues = transformAttributes( - toParameters(attributeValues, - this.clazz, - getTableName(), - saveConfig)); - PutItemRequest req = PutItemRequest.builder() - .tableName(getTableName()) - .item(attributeValues) - .expected(mergeExpectedAttributeValueConditions()) - .conditionalOperator(userProvidedConditionOperator) - .build(); - - return db.putItem(applyUserAgent(req)); - } - - /** - * Auto-generates the attribute value. - * @param field The mapping details. - */ - private void onAutoGenerate(DynamoDbMapperFieldModel field) { - AttributeValue value = field.convert(field.generate(field.get(object))); - updateValues.put(field.name(), AttributeValueUpdate.builder().action("PUT").value(value).build()); - inMemoryUpdates.add(new ValueUpdate(field, value, object)); - } - - /** - * Auto-generates the key. - */ - private void onAutoGenerateAssignableKey(DynamoDbMapperFieldModel field) { - // Generate the new key value first, then ensure it doesn't exist. - onAutoGenerate(field); - - if (localSaveBehavior() != SaveBehavior.CLOBBER - && !internalExpectedValueAssertions.containsKey(field.name()) - && field.getGenerateStrategy() != DynamoDbAutoGenerateStrategy.ALWAYS) { - // Add an expect clause to make sure that the item - // doesn't already exist, since it's supposed to be new - internalExpectedValueAssertions.put(field.name(), - ExpectedAttributeValue.builder() - .exists(false) - .build()); - } - } - - /** - * Auto-generates the version. - * @param field The mapping details. - */ - private void onVersionAttribute(DynamoDbMapperFieldModel field) { - if (localSaveBehavior() != SaveBehavior.CLOBBER - && !internalExpectedValueAssertions.containsKey(field.name())) { - // First establish the expected (current) value for the - // update call - // For new objects, insist that the value doesn't exist. - // For existing ones, insist it has the old value. - final Object current = field.get(object); - if (current == null) { - internalExpectedValueAssertions.put(field.name(), - ExpectedAttributeValue.builder() - .exists(false) - .build()); - } else { - internalExpectedValueAssertions.put(field.name(), - ExpectedAttributeValue.builder() - .exists(true) - .value(field.convert(current)) - .build()); - } - } - - // Generate the new version value - onAutoGenerate(field); - } - - /** - * Converts the {@link AttributeValueUpdate} map given to an equivalent - * {@link AttributeValue} map. - */ - private Map convertToItem(Map putValues) { - Map map = new HashMap(); - for (Entry entry : putValues.entrySet()) { - String attributeName = entry.getKey(); - AttributeValue attributeValue = entry.getValue().value(); - String attributeAction = entry.getValue().actionAsString(); - - /* - * AttributeValueUpdate allows nulls for its values, since they are - * semantically meaningful. AttributeValues never have null values. - */ - if (attributeValue != null - && !AttributeAction.DELETE.toString().equals(attributeAction)) { - map.put(attributeName, attributeValue); - } - } - return map; - } - - private Map transformAttributeUpdates( - final Class clazz, - final String tableName, - final Map keys, - final Map updateValues, - final DynamoDbMapperConfig config) { - Map item = convertToItem(updateValues); - - HashSet keysAdded = new HashSet(); - for (Map.Entry e : keys.entrySet()) { - if (!item.containsKey(e.getKey())) { - keysAdded.add(e.getKey()); - item.put(e.getKey(), e.getValue()); - } - } - - AttributeTransformer.Parameters parameters = - toParameters(item, true, clazz, tableName, config); - - String hashKey = parameters.getHashKeyName(); - - if (!item.containsKey(hashKey)) { - item.put(hashKey, keys.get(hashKey)); - } - - item = transformAttributes(parameters); - - for (Map.Entry entry : item.entrySet()) { - if (keysAdded.contains(entry.getKey())) { - // This was added in for context before calling - // transformAttributes, but isn't actually being changed. - continue; - } - - AttributeValueUpdate update = updateValues.get(entry.getKey()); - if (update != null) { - AttributeValue value = update.value().toBuilder() - .b(entry.getValue().b()) - .bs(entry.getValue().bs()) - .n(entry.getValue().n()) - .ns(entry.getValue().ns()) - .s(entry.getValue().s()) - .ss(entry.getValue().ss()) - .m(entry.getValue().m()) - .l(entry.getValue().l()) - .nul(entry.getValue().nul()) - .bool(entry.getValue().bool()).build(); - - update = update.toBuilder().value(value).build(); - updateValues.put(entry.getKey(), update); - } else { - updateValues.put(entry.getKey(), AttributeValueUpdate.builder() - .value(entry.getValue()) - .action("PUT") - .build()); - } - } - - return updateValues; - } - } - - private static final class ValueUpdate { - private final DynamoDbMapperFieldModel field; - private final AttributeValue newValue; - private final Object target; - - ValueUpdate( - DynamoDbMapperFieldModel field, - AttributeValue newValue, - Object target) { - - this.field = field; - this.newValue = newValue; - this.target = target; - } - - public void apply() { - field.set(target, field.unconvert(newValue)); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperConfig.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperConfig.java deleted file mode 100644 index 783d51e814e5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperConfig.java +++ /dev/null @@ -1,1134 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.security.SecureRandom; -import java.util.List; -import java.util.Map; -import java.util.Random; -import software.amazon.awssdk.services.dynamodb.model.KeysAndAttributes; -import software.amazon.awssdk.services.dynamodb.model.WriteRequest; - -/** - * Immutable configuration object for service call behavior. An instance of this - * configuration is supplied to every {@link DynamoDbMapper} at construction; if - * not provided explicitly, {@link DynamoDbMapperConfig#DEFAULT} is used. New - * instances can be given to the mapper object on individual save, load, and - * delete operations to override the defaults. For example: - * - *
    - * DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
    - * // Force this read to be consistent
    - * DomainClass obj = mapper.load(DomainClass.class, key, ConsistentRead.CONSISTENT.config());
    - * // Force this save operation to use putItem rather than updateItem
    - * mapper.save(obj, SaveBehavior.CLOBBER.config());
    - * // Save the object into a different table
    - * mapper.save(obj, new TableNameOverride("AnotherTable").config());
    - * // Delete the object even if the version field is out of date
    - * mapper.delete(obj, SaveBehavior.CLOBBER.config());
    - * 
    - */ -public class DynamoDbMapperConfig { - - /** - * Default configuration; these defaults are also applied by the mapper - * when only partial configurations are specified. - * - * @see SaveBehavior#UPDATE - * @see ConsistentRead#EVENTUAL - * @see PaginationLoadingStrategy#LAZY_LOADING - * @see DefaultTableNameResolver#INSTANCE - * @see DefaultBatchWriteRetryStrategy#INSTANCE - * @see DefaultBatchLoadRetryStrategy#INSTANCE - * @see DynamoDbTypeConverterFactory#standard - * @see ConversionSchemas#DEFAULT - */ - public static final DynamoDbMapperConfig DEFAULT = builder() - .withSaveBehavior(SaveBehavior.UPDATE) - .withConsistentReads(ConsistentRead.EVENTUAL) - .withPaginationLoadingStrategy(PaginationLoadingStrategy.LAZY_LOADING) - .withTableNameResolver(DefaultTableNameResolver.INSTANCE) - .withBatchWriteRetryStrategy(DefaultBatchWriteRetryStrategy.INSTANCE) - .withBatchLoadRetryStrategy(DefaultBatchLoadRetryStrategy.INSTANCE) - .withTypeConverterFactory(DynamoDbTypeConverterFactory.standard()) - .withConversionSchema(ConversionSchemas.DEFAULT) - .build(); - private final SaveBehavior saveBehavior; - private final ConsistentRead consistentRead; - private final TableNameOverride tableNameOverride; - private final TableNameResolver tableNameResolver; - private final ObjectTableNameResolver objectTableNameResolver; - private final PaginationLoadingStrategy paginationLoadingStrategy; - private final ConversionSchema conversionschema; - private final BatchWriteRetryStrategy batchWriteRetryStrategy; - private final BatchLoadRetryStrategy batchLoadRetryStrategy; - private final DynamoDbTypeConverterFactory typeConverterFactory; - - /** - * Internal constructor; builds from the builder. - */ - private DynamoDbMapperConfig(final DynamoDbMapperConfig.Builder builder) { - this.saveBehavior = builder.saveBehavior; - this.consistentRead = builder.consistentRead; - this.tableNameOverride = builder.tableNameOverride; - this.tableNameResolver = builder.tableNameResolver; - this.objectTableNameResolver = builder.objectTableNameResolver; - this.paginationLoadingStrategy = builder.paginationLoadingStrategy; - this.conversionschema = builder.conversionschema; - this.batchWriteRetryStrategy = builder.batchWriteRetryStrategy; - this.batchLoadRetryStrategy = builder.batchLoadRetryStrategy; - this.typeConverterFactory = builder.typeConverterFactory; - } - - private DynamoDbMapperConfig( - SaveBehavior saveBehavior, - ConsistentRead consistentRead, - TableNameOverride tableNameOverride, - TableNameResolver tableNameResolver, - ObjectTableNameResolver objectTableNameResolver, - PaginationLoadingStrategy paginationLoadingStrategy, - ConversionSchema conversionschema, - BatchWriteRetryStrategy batchWriteRetryStrategy, - BatchLoadRetryStrategy batchLoadRetryStrategy) { - - this.saveBehavior = saveBehavior; - this.consistentRead = consistentRead; - this.tableNameOverride = tableNameOverride; - this.tableNameResolver = tableNameResolver; - this.objectTableNameResolver = objectTableNameResolver; - this.paginationLoadingStrategy = paginationLoadingStrategy; - this.conversionschema = conversionschema; - this.batchWriteRetryStrategy = batchWriteRetryStrategy; - this.batchLoadRetryStrategy = batchLoadRetryStrategy; - this.typeConverterFactory = null; - } - - /** - * Constructs a new configuration object with the save behavior given. - * @see SaveBehavior#config - */ - @Deprecated - public DynamoDbMapperConfig(SaveBehavior saveBehavior) { - this(saveBehavior, null, null, null, null, null, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the consistent read behavior - * given. - * @see ConsistentRead#config - */ - @Deprecated - public DynamoDbMapperConfig(ConsistentRead consistentRead) { - this(null, consistentRead, null, null, null, null, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the table name override given. - * @see TableNameOverride#config - */ - @Deprecated - public DynamoDbMapperConfig(TableNameOverride tableNameOverride) { - this(null, null, tableNameOverride, null, null, null, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the table name resolver strategy given. - * @see DynamoDBConfig#builder - */ - @Deprecated - public DynamoDbMapperConfig(TableNameResolver tableNameResolver) { - this(null, null, null, tableNameResolver, null, null, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the object table name resolver strategy given. - * @see DynamoDBConfig#builder - */ - @Deprecated - public DynamoDbMapperConfig(ObjectTableNameResolver objectTableNameResolver) { - this(null, null, null, null, objectTableNameResolver, null, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the table name resolver strategies given. - * @see DynamoDBConfig#builder - */ - @Deprecated - public DynamoDbMapperConfig(TableNameResolver tableNameResolver, ObjectTableNameResolver objectTableNameResolver) { - this(null, null, null, tableNameResolver, objectTableNameResolver, null, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the pagination loading - * strategy given. - * @see PaginationLoadingStrategy#config - */ - @Deprecated - public DynamoDbMapperConfig( - PaginationLoadingStrategy paginationLoadingStrategy) { - - this(null, null, null, null, null, paginationLoadingStrategy, - DEFAULT.getConversionSchema(), DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object with the conversion schema given. - * @see DynamoDBConfig#builder - */ - @Deprecated - public DynamoDbMapperConfig(ConversionSchema conversionschema) { - this(null, null, null, null, null, null, - conversionschema, DEFAULT.batchWriteRetryStrategy(), DEFAULT.batchLoadRetryStrategy()); - } - - /** - * Constructs a new configuration object from two others: a set of defaults - * and a set of overrides. Any non-null overrides will be applied to the - * defaults. - *

    - * Used internally to merge the {@link DynamoDbMapperConfig} provided at - * construction with an overriding object for a particular operation. - * - * @param defaults - * The default mapper configuration values. - * @param overrides - * The overridden mapper configuration values. Any non-null - * config settings will be applied to the returned object. - * @see DynamoDBConfig#builder - */ - @Deprecated - public DynamoDbMapperConfig( - DynamoDbMapperConfig defaults, - DynamoDbMapperConfig overrides) { - this(builder().merge(defaults).merge(overrides)); - } - - /** - * Creates a new empty builder. - */ - public static Builder builder() { - return new Builder(false); - } - - /** - * Merges these configuration values with the specified overrides; may - * simply return this instance if overrides are the same or null. - * @param overrides The overrides to merge. - * @return This if the overrides are same or null, or a new merged config. - */ - final DynamoDbMapperConfig merge(final DynamoDbMapperConfig overrides) { - return overrides == null || this.equals(overrides) ? this : builder().merge(this).merge(overrides).build(); - } - - public BatchLoadRetryStrategy batchLoadRetryStrategy() { - return batchLoadRetryStrategy; - } - - /** - * Returns the save behavior for this configuration. - */ - public SaveBehavior saveBehavior() { - return saveBehavior; - } - - /** - * Returns the consistent read behavior for this configuration. - */ - public ConsistentRead getConsistentRead() { - return consistentRead; - } - - /** - * Returns the table name override for this configuration. This value will - * override the table name specified in a {@link DynamoDbTable} annotation, - * either by replacing the table name entirely or else by pre-pending a - * string to each table name. This is useful for partitioning data in - * multiple tables at runtime. - * - * @see TableNameOverride#withTableNamePrefix(String) - * @see TableNameOverride#withTableNameReplacement(String) - */ - public TableNameOverride getTableNameOverride() { - return tableNameOverride; - } - - /** - * Returns the table name resolver for this configuration. This value will - * be used to determine the table name for classes. It can be - * used for more powerful customization of table name than is possible using - * only {@link TableNameOverride}. - * - * @see TableNameResolver#getTableName(Class, DynamoDbMapperConfig) - */ - public TableNameResolver getTableNameResolver() { - return tableNameResolver; - } - - /** - * Returns the object table name resolver for this configuration. This value will - * be used to determine the table name for objects. It can be - * used for more powerful customization of table name than is possible using - * only {@link TableNameOverride}. - * - * @see ObjectTableNameResolver#getTableName(Object, DynamoDbMapperConfig) - */ - public ObjectTableNameResolver getObjectTableNameResolver() { - return objectTableNameResolver; - } - - /** - * Returns the pagination loading strategy for this configuration. - */ - public PaginationLoadingStrategy getPaginationLoadingStrategy() { - return paginationLoadingStrategy; - } - - /** - * @return the conversion schema for this config object - */ - public ConversionSchema getConversionSchema() { - return conversionschema; - } - - /** - * @return the BatchWriteRetryStrategy for this config object - */ - public BatchWriteRetryStrategy batchWriteRetryStrategy() { - return batchWriteRetryStrategy; - } - - /** - * @return the current type-converter factory - */ - public final DynamoDbTypeConverterFactory getTypeConverterFactory() { - return typeConverterFactory; - } - - /** - * Enumeration of behaviors for the save operation. - */ - public enum SaveBehavior { - /** - * UPDATE will not affect unmodeled attributes on a save operation and a - * null value for the modeled attribute will remove it from that item in - * DynamoDB. - *

    - * Because of the limitation of updateItem request, the implementation - * of UPDATE will send a putItem request when a key-only object is being - * saved, and it will send another updateItem request if the given - * key(s) already exists in the table. - *

    - * By default, the mapper uses UPDATE. - */ - UPDATE, - - /** - * UPDATE_SKIP_NULL_ATTRIBUTES is similar to UPDATE, except that it - * ignores any null value attribute(s) and will NOT remove them from - * that item in DynamoDB. It also guarantees to send only one single - * updateItem request, no matter the object is key-only or not. - */ - UPDATE_SKIP_NULL_ATTRIBUTES, - - /** - * CLOBBER will clear and replace all attributes, included unmodeled - * ones, (delete and recreate) on save. Versioned field constraints will - * also be disregarded. - */ - CLOBBER, - - /** - * APPEND_SET treats scalar attributes (String, Number, Binary) the same - * as UPDATE_SKIP_NULL_ATTRIBUTES does. However, for set attributes, it - * will append to the existing attribute value, instead of overriding - * it. Caller needs to make sure that the modeled attribute type matches - * the existing set type, otherwise it would result in a service - * exception. - */ - APPEND_SET; - - private final DynamoDbMapperConfig config = builder().withSaveBehavior(this).build(); - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * Enumeration of consistent read behavior. - *

    - * CONSISTENT uses consistent reads, EVENTUAL does not. Consistent reads - * have implications for performance and billing; see the service - * documentation for details. - *

    - * By default, the mapper uses eventual consistency. - */ - public enum ConsistentRead { - CONSISTENT, - EVENTUAL; - - private final DynamoDbMapperConfig config = builder().withConsistentReads(this).build(); - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * Enumeration of pagination loading strategy. - */ - public enum PaginationLoadingStrategy { - /** - * Paginated list is lazily loaded when possible, and all loaded results - * are kept in the memory. - *

    - * By default, the mapper uses LAZY_LOADING. - */ - LAZY_LOADING, - - /** - * Only supports using iterator to read from the paginated list. All - * other list operations will return UnsupportedOperationException - * immediately. During the iteration, the list will clear all the - * previous results before loading the next page, so that the list will - * keep at most one page of the loaded results in memory. This also - * means the list could only be iterated once. - *

    - * Use this configuration to reduce the memory overhead when handling - * large DynamoDB items. - */ - ITERATION_ONLY, - - /** - * Paginated list will eagerly load all the paginated results from - * DynamoDB as soon as the list is initialized. - */ - EAGER_LOADING; - - private final DynamoDbMapperConfig config = builder().withPaginationLoadingStrategy(this).build(); - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * Interface for a strategy used to determine the table name of an object based on it's class. - * This resolver is used when an object isn't available such as in - * {@link DynamoDbMapper#query(Class, DynamoDbQueryExpression)} - * - * @see ObjectTableNameResolver - * @author Raniz - */ - public interface TableNameResolver { - - /** - * Get the table name for a class. This method is used when an object is not available - * such as when creating requests for scan or query operations. - * - * @param clazz The class to get the table name for - * @param config The {@link DynamoDbMapperConfig} - * @return The table name to use for instances of clazz - */ - String getTableName(Class clazz, DynamoDbMapperConfig config); - } - - /** - * Interface for a strategy used to determine the table name of an object based on it's class. - * This resolver is used when an object is available such as in - * {@link DynamoDbMapper#316 - * (java.util.List)}. - * - * If no table name resolver for objects is set, {@link DynamoDbMapper} reverts to using the - * {@link TableNameResolver} on each object's class. - * - * @see TableNameResolver - * @author Raniz - */ - public interface ObjectTableNameResolver { - - /** - * Get the table name for an object. - * - * @param object The object to get the table name for - * @param config The {@link DynamoDbMapperConfig} - * @return The table name to use for object - */ - String getTableName(Object object, DynamoDbMapperConfig config); - - } - - /** - * DynamoDBMapper#batchWrite takes arbitrary number of save/delete requests - * and breaks them into smaller chunks that can be accepted by the service - * API. Each chunk will be sent to DynamoDB via the BatchWriteItem API, and - * if it fails because the table's provisioned throughput is exceeded or an - * internal processing failure occurs, the failed requests are returned in - * the UnprocessedItems response parameter. This interface allows you to - * control the retry strategy when such scenario occurs. - * - * @see DynamoDbMapper#batchWrite(List, List, DynamoDbMapperConfig) - * @see DynamoDB service API reference -- BatchWriteItem - */ - public interface BatchWriteRetryStrategy { - - /** - * Returns the max number of retries to be performed if the service - * returns UnprocessedItems in the response. - * - * @param batchWriteItemInput - * the one batch of write requests that is being sent to the - * BatchWriteItem API. - * @return max number of retries to be performed if the service returns - * UnprocessedItems in the response, or a negative value if you - * want it to keep retrying until all the UnprocessedItems are - * fulfilled. - */ - int maxRetryOnUnprocessedItems( - Map> batchWriteItemInput); - - /** - * Returns the delay (in milliseconds) before retrying on - * UnprocessedItems. - * - * @param unprocessedItems - * the UnprocessedItems returned by the service in the last - * BatchWriteItem call - * @param retriesAttempted - * The number of times we have attempted to resend - * UnprocessedItems. - * @return the delay (in milliseconds) before resending - * UnprocessedItems. - */ - long getDelayBeforeRetryUnprocessedItems( - Map> unprocessedItems, - int retriesAttempted); - } - - /** - * {@link DynamoDbMapper#batchLoad(List)} breaks the requested items in batches of maximum size 100. - * When calling the Dyanmo Db client, there is a chance that due to throttling, some unprocessed keys will be returned. - * This interfaces controls whether we need to retry these unprocessed keys and it also controls the strategy as to how - * retries should be handled. - */ - public interface BatchLoadRetryStrategy { - /** - * Checks if the batch load request should be retried. - * @param batchLoadContext see {@link BatchLoadContext} - * - * @return a boolean true or false value. - */ - boolean shouldRetry(BatchLoadContext batchLoadContext); - - /** - * Returns delay(in milliseconds) before retrying Unprocessed keys - * - * @param batchLoadContext see {@link BatchLoadContext} - * @return delay(in milliseconds) before attempting to read unprocessed keys - */ - long getDelayBeforeNextRetry(BatchLoadContext batchLoadContext); - } - - /** - * A fluent builder for DynamoDBMapperConfig objects. - */ - public static class Builder { - - private SaveBehavior saveBehavior; - private ConsistentRead consistentRead; - private TableNameOverride tableNameOverride; - private TableNameResolver tableNameResolver; - private ObjectTableNameResolver objectTableNameResolver; - private PaginationLoadingStrategy paginationLoadingStrategy; - private ConversionSchema conversionschema; - private BatchWriteRetryStrategy batchWriteRetryStrategy; - private BatchLoadRetryStrategy batchLoadRetryStrategy; - private DynamoDbTypeConverterFactory typeConverterFactory; - - /** - * Creates a new builder initialized with the {@link #DEFAULT} values. - */ - public Builder() { - this(true); - } - - /** - * Creates a new builder, optionally initialized with the defaults. - */ - private Builder(final boolean defaults) { - if (defaults == true) { - saveBehavior = DEFAULT.saveBehavior(); - consistentRead = DEFAULT.getConsistentRead(); - paginationLoadingStrategy = DEFAULT.getPaginationLoadingStrategy(); - conversionschema = DEFAULT.getConversionSchema(); - batchWriteRetryStrategy = DEFAULT.batchWriteRetryStrategy(); - batchLoadRetryStrategy = DEFAULT.batchLoadRetryStrategy(); - } - } - - /** - * Merges any non-null configuration values for the specified overrides. - */ - private Builder merge(final DynamoDbMapperConfig o) { - if (o == null) { - return this; - } - if (o.saveBehavior != null) { - saveBehavior = o.saveBehavior; - } - if (o.consistentRead != null) { - consistentRead = o.consistentRead; - } - if (o.tableNameOverride != null) { - tableNameOverride = o.tableNameOverride; - } - if (o.tableNameResolver != null) { - tableNameResolver = o.tableNameResolver; - } - if (o.objectTableNameResolver != null) { - objectTableNameResolver = o.objectTableNameResolver; - } - if (o.paginationLoadingStrategy != null) { - paginationLoadingStrategy = o.paginationLoadingStrategy; - } - if (o.conversionschema != null) { - conversionschema = o.conversionschema; - } - if (o.batchWriteRetryStrategy != null) { - batchWriteRetryStrategy = o.batchWriteRetryStrategy; - } - if (o.batchLoadRetryStrategy != null) { - batchLoadRetryStrategy = o.batchLoadRetryStrategy; - } - if (o.typeConverterFactory != null) { - typeConverterFactory = o.typeConverterFactory; - } - return this; - } - - /** - * @return the currently-configured save behavior - */ - public SaveBehavior saveBehavior() { - return saveBehavior; - } - - /** - * @param value the new save behavior - */ - public void setSaveBehavior(SaveBehavior value) { - saveBehavior = value; - } - - /** - * @param value the new save behavior - * @return this builder - */ - public Builder withSaveBehavior(SaveBehavior value) { - setSaveBehavior(value); - return this; - } - - - /** - * Returns the consistent read behavior. Currently - * this value is applied only in load and batch load operations of the - * DynamoDBMapper. - * @return the currently-configured consistent read behavior. - */ - public ConsistentRead getConsistentRead() { - return consistentRead; - } - - /** - * Sets the consistent read behavior. Currently - * this value is applied only in load and batch load operations of the - * DynamoDBMapper. - * @param value the new consistent read behavior. - */ - public void setConsistentRead(ConsistentRead value) { - consistentRead = value; - } - - /** - * Sets the consistent read behavior. Currently - * this value is applied only in load and batch load operations of the - * DynamoDBMapper. - * @param value the new consistent read behavior - * @return this builder. - * - */ - public Builder withConsistentReads(ConsistentRead value) { - setConsistentRead(value); - return this; - } - - - /** - * @return the current table name override - */ - public TableNameOverride getTableNameOverride() { - return tableNameOverride; - } - - /** - * @param value the new table name override - */ - public void setTableNameOverride(TableNameOverride value) { - tableNameOverride = value; - } - - /** - * @param value the new table name override - * @return this builder - */ - public Builder withTableNameOverride(TableNameOverride value) { - setTableNameOverride(value); - return this; - } - - - /** - * @return the current table name resolver - */ - public TableNameResolver getTableNameResolver() { - return tableNameResolver; - } - - /** - * @param value the new table name resolver - */ - public void setTableNameResolver(TableNameResolver value) { - tableNameResolver = value; - } - - /** - * @param value the new table name resolver - * @return this builder - */ - public Builder withTableNameResolver(TableNameResolver value) { - setTableNameResolver(value); - return this; - } - - - /** - * @return the current object table name resolver - */ - public ObjectTableNameResolver getObjectTableNameResolver() { - return objectTableNameResolver; - } - - /** - * @param value the new object table name resolver - */ - public void setObjectTableNameResolver(ObjectTableNameResolver value) { - objectTableNameResolver = value; - } - - /** - * @param value the new object table name resolver - * @return this builder - */ - public Builder withObjectTableNameResolver(ObjectTableNameResolver value) { - setObjectTableNameResolver(value); - return this; - } - - /** - * @return the currently-configured pagination loading strategy - */ - public PaginationLoadingStrategy getPaginationLoadingStrategy() { - return paginationLoadingStrategy; - } - - /** - * @param value the new pagination loading strategy - */ - public void setPaginationLoadingStrategy( - PaginationLoadingStrategy value) { - - paginationLoadingStrategy = value; - } - - /** - * @param value the new pagination loading strategy - * @return this builder - */ - public Builder withPaginationLoadingStrategy( - PaginationLoadingStrategy value) { - - setPaginationLoadingStrategy(value); - return this; - } - - /** - * @return the current conversion schema - */ - public ConversionSchema getConversionSchema() { - return conversionschema; - } - - /** - * @param value the new conversion schema - */ - public void setConversionSchema(ConversionSchema value) { - conversionschema = value; - } - - /** - * @param value the new conversion schema - * @return this builder - */ - public Builder withConversionSchema(ConversionSchema value) { - setConversionSchema(value); - return this; - } - - /** - * @return the current BatchWriteRetryStrategy - */ - public BatchWriteRetryStrategy batchWriteRetryStrategy() { - return batchWriteRetryStrategy; - } - - /** - * @param value the new BatchWriteRetryStrategy - */ - public void setBatchWriteRetryStrategy( - BatchWriteRetryStrategy value) { - this.batchWriteRetryStrategy = value; - } - - /** - * @param value the new BatchWriteRetryStrategy - * @return this builder - */ - public Builder withBatchWriteRetryStrategy( - BatchWriteRetryStrategy value) { - setBatchWriteRetryStrategy(value); - return this; - } - - public BatchLoadRetryStrategy batchLoadRetryStrategy() { - return batchLoadRetryStrategy; - } - - /** - * @param value the new BatchLoadRetryStrategy - */ - public void setBatchLoadRetryStrategy( - BatchLoadRetryStrategy value) { - this.batchLoadRetryStrategy = value; - } - - /** - * @param value the new BatchLoadRetryStrategy - * @return this builder - */ - public Builder withBatchLoadRetryStrategy( - BatchLoadRetryStrategy value) { - //set the no retry strategy if the user overrides the default with null - if (value == null) { - value = NoRetryBatchLoadRetryStrategy.INSTANCE; - } - setBatchLoadRetryStrategy(value); - return this; - } - - /** - * @return the current type-converter factory - */ - public final DynamoDbTypeConverterFactory getTypeConverterFactory() { - return typeConverterFactory; - } - - /** - * @param value the new type-converter factory - */ - public final void setTypeConverterFactory(DynamoDbTypeConverterFactory value) { - this.typeConverterFactory = value; - } - - /** - * The type-converter factory for scalar conversions. - *

    To override standard type-conversions,

    - *
    -         * DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    -         *     .withTypeConverterFactory(DynamoDBTypeConverterFactory.standard().override()
    -         *         .with(String.class, MyObject.class, new StringToMyObjectConverter())
    -         *         .build())
    -         *     .build();
    -         * 
    - *

    Then, on the property, specify the attribute binding,

    - *
    -         * @DynamoDBTyped(DynamoDBAttributeType.S)
    -         * public MyObject myObject()
    -         * 
    - * @param value the new type-converter factory - * @return this builder - */ - public final Builder withTypeConverterFactory(DynamoDbTypeConverterFactory value) { - setTypeConverterFactory(value); - return this; - } - - /** - * Builds a new {@code DynamoDBMapperConfig} object. - * - * @return the new, immutable config object - */ - public DynamoDbMapperConfig build() { - return new DynamoDbMapperConfig(this); - } - } - - /** - * Allows overriding the table name declared on a domain class by the - * {@link DynamoDbTable} annotation. - */ - public static final class TableNameOverride { - - private final String tableNameOverride; - private final String tableNamePrefix; - private final DynamoDbMapperConfig config = builder().withTableNameOverride(this).build(); - - private TableNameOverride(String tableNameOverride, String tableNamePrefix) { - this.tableNameOverride = tableNameOverride; - this.tableNamePrefix = tableNamePrefix; - } - - /** - * @see TableNameOverride#withTableNameReplacement(String) - */ - public TableNameOverride(String tableNameOverride) { - this(tableNameOverride, null); - } - - /** - * Returns a new {@link TableNameOverride} object that will prepend the - * given string to every table name. - */ - public static TableNameOverride withTableNamePrefix( - String tableNamePrefix) { - - return new TableNameOverride(null, tableNamePrefix); - } - - /** - * Returns a new {@link TableNameOverride} object that will replace - * every table name in requests with the given string. - */ - public static TableNameOverride withTableNameReplacement( - String tableNameReplacement) { - - return new TableNameOverride(tableNameReplacement, null); - } - - /** - * Returns the table name to use for all requests. Exclusive with - * {@link TableNameOverride#getTableNamePrefix()} - * - * @see DynamoDbMapperConfig#getTableNameOverride() - */ - public String getTableName() { - return tableNameOverride; - } - - /** - * Returns the table name prefix to prepend the table name for all - * requests. Exclusive with {@link TableNameOverride#getTableName()} - * - * @see DynamoDbMapperConfig#getTableNameOverride() - */ - public String getTableNamePrefix() { - return tableNamePrefix; - } - - public DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * Default implementation of {@link TableNameResolver} that mimics the behavior - * of DynamoDBMapper before the addition of {@link TableNameResolver}. - * - * @author Raniz - */ - public static class DefaultTableNameResolver implements TableNameResolver { - public static final DefaultTableNameResolver INSTANCE = new DefaultTableNameResolver(); - private final DynamoDbMapperConfig config = builder().withTableNameResolver(this).build(); - - @Override - public String getTableName(Class clazz, DynamoDbMapperConfig config) { - final TableNameOverride override = config.getTableNameOverride(); - - if (override != null) { - final String tableName = override.getTableName(); - if (tableName != null) { - return tableName; - } - } - - final StandardBeanProperties.Beans beans = StandardBeanProperties.of(clazz); - if (beans.properties().tableName() == null) { - throw new DynamoDbMappingException(clazz + " not annotated with @DynamoDBTable"); - } - - final String prefix = override == null ? null : override.getTableNamePrefix(); - return prefix == null ? beans.properties().tableName() : prefix + beans.properties().tableName(); - } - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * This strategy, like name suggests will not attempt any retries on Unprocessed keys - * - * @author smihir - * - */ - public static class NoRetryBatchLoadRetryStrategy implements BatchLoadRetryStrategy { - public static final NoRetryBatchLoadRetryStrategy INSTANCE = new NoRetryBatchLoadRetryStrategy(); - private final DynamoDbMapperConfig config = builder().withBatchLoadRetryStrategy(this).build(); - - /* (non-Javadoc) - * @see BatchLoadRetryStrategy#maxRetryOnUnprocessedKeys(java.util.Map, java.util.Map) - */ - @Override - public boolean shouldRetry(final BatchLoadContext batchLoadContext) { - return false; - } - - /* (non-Javadoc) - * @see BatchLoadRetryStrategy#getDelayBeforeNextRetry(java.util.Map, int) - */ - @Override - public long getDelayBeforeNextRetry(final BatchLoadContext batchLoadContext) { - return -1; - } - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * This is the default strategy. - * If unprocessed keys is equal to requested keys, the request will retried 5 times with a back off strategy - * with maximum back off of 3 seconds - * If few of the keys have been processed, the retries happen without a delay. - * - * @author smihir - * - */ - public static class DefaultBatchLoadRetryStrategy implements BatchLoadRetryStrategy { - public static final DefaultBatchLoadRetryStrategy INSTANCE = new DefaultBatchLoadRetryStrategy(); - - private static final int MAX_RETRIES = 5; - private static final long MAX_BACKOFF_IN_MILLISECONDS = 1000 * 3L; - private final DynamoDbMapperConfig config = builder().withBatchLoadRetryStrategy(this).build(); - - @Override - public long getDelayBeforeNextRetry(final BatchLoadContext batchLoadContext) { - Map requestedKeys = batchLoadContext.batchGetItemRequest().requestItems(); - Map unprocessedKeys = batchLoadContext.batchGetItemResponse() - .unprocessedKeys(); - - long delay = 0; - //Exponential backoff only when all keys are unprocessed - if (unprocessedKeys != null && requestedKeys != null && unprocessedKeys.size() == requestedKeys.size()) { - Random random = new SecureRandom(); - long scaleFactor = 500L + random.nextInt(100); - int retriesAttempted = batchLoadContext.getRetriesAttempted(); - delay = (long) (Math.pow(2, retriesAttempted) * scaleFactor); - delay = Math.min(delay, MAX_BACKOFF_IN_MILLISECONDS); - } - return delay; - } - - @Override - public boolean shouldRetry(BatchLoadContext batchLoadContext) { - Map unprocessedKeys = batchLoadContext.batchGetItemResponse().unprocessedKeys(); - return unprocessedKeys != null && unprocessedKeys.size() > 0 && batchLoadContext.getRetriesAttempted() < MAX_RETRIES; - } - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - - /** - * The default BatchWriteRetryStrategy which always retries on - * UnprocessedItem up to a maximum number of times and use exponential - * backoff with random scale factor. - */ - public static class DefaultBatchWriteRetryStrategy implements BatchWriteRetryStrategy { - public static final DefaultBatchWriteRetryStrategy INSTANCE = new DefaultBatchWriteRetryStrategy(); - - private static final long MAX_BACKOFF_IN_MILLISECONDS = 1_000 * 3L; - private static final int DEFAULT_MAX_RETRY = -1; - - private final int maxRetry; - private final DynamoDbMapperConfig config = builder().withBatchWriteRetryStrategy(this).build(); - - /** - * Keep retrying until success, with default backoff. - */ - public DefaultBatchWriteRetryStrategy() { - this(DEFAULT_MAX_RETRY); - } - - public DefaultBatchWriteRetryStrategy(int maxRetry) { - this.maxRetry = maxRetry; - } - - @Override - public int maxRetryOnUnprocessedItems( - Map> batchWriteItemInput) { - return maxRetry; - } - - @Override - public long getDelayBeforeRetryUnprocessedItems( - Map> unprocessedItems, - int retriesAttempted) { - - if (retriesAttempted < 0) { - return 0; - } - - Random random = new SecureRandom(); - long scaleFactor = 1_000L + random.nextInt(200); - long delay = (long) (Math.pow(2, retriesAttempted) * scaleFactor); - return Math.min(delay, MAX_BACKOFF_IN_MILLISECONDS); - } - - public final DynamoDbMapperConfig config() { - return this.config; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperFieldModel.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperFieldModel.java deleted file mode 100644 index 991f2d32a03d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperFieldModel.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGenerateStrategy.ALWAYS; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Vector.LIST; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.BEGINS_WITH; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.BETWEEN; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.CONTAINS; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.EQ; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.GE; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.GT; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.IN; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.LE; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.LT; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.NE; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.NOT_CONTAINS; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.NOT_NULL; -import static software.amazon.awssdk.services.dynamodb.model.ComparisonOperator.NULL; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.KeyType; - -/** - * Field model. - * - * @param The object type. - * @param The field model type. - */ -public class DynamoDbMapperFieldModel implements DynamoDbAutoGenerator, DynamoDbTypeConverter { - - private final DynamoDbMapperFieldModel.Properties properties; - - - private final DynamoDbTypeConverter converter; - private final DynamoDbAttributeType attributeType; - private final DynamoDbMapperFieldModel.Reflect reflect; - - /** - * Creates a new field model instance. - * @param builder The builder. - */ - private DynamoDbMapperFieldModel(final DynamoDbMapperFieldModel.Builder builder) { - this.properties = builder.properties; - this.converter = builder.converter; - this.attributeType = builder.attributeType; - this.reflect = builder.reflect; - } - - /** - * @deprecated replaced by {@link DynamoDbMapperFieldModel#name} - */ - @Deprecated - public String getDynamoDbAttributeName() { - return properties.attributeName(); - } - - /** - * @deprecated replaced by {@link DynamoDbMapperFieldModel#attributeType} - */ - @Deprecated - public DynamoDbAttributeType getDynamoDbAttributeType() { - return attributeType; - } - - /** - * Gets the attribute name. - * @return The attribute name. - */ - public final String name() { - return properties.attributeName(); - } - - /** - * Gets the value from the object instance. - * @param object The object instance. - * @return The value. - */ - public final V get(final T object) { - return reflect.get(object); - } - - /** - * Sets the value on the object instance. - * @param object The object instance. - * @param value The value. - */ - public final void set(final T object, final V value) { - reflect.set(object, value); - } - - /** - * {@inheritDoc} - */ - @Override - public final DynamoDbAutoGenerateStrategy getGenerateStrategy() { - if (properties.autoGenerator() != null) { - return properties.autoGenerator().getGenerateStrategy(); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public final V generate(final V currentValue) { - return properties.autoGenerator().generate(currentValue); - } - - /** - * {@inheritDoc} - */ - @Override - public final AttributeValue convert(final V object) { - AttributeValue v = converter.convert(object); - return v; - } - - /** - * {@inheritDoc} - */ - @Override - public final V unconvert(final AttributeValue object) { - return converter.unconvert(object); - } - - /** - * Get the current value from the object and convert it. - * @param object The object instance. - * @return The converted value. - */ - public final AttributeValue getAndConvert(final T object) { - return convert(get(object)); - } - - /** - * Unconverts the value and sets it on the object. - * @param object The object instance. - * @param value The attribute value. - */ - public final void unconvertAndSet(final T object, final AttributeValue value) { - set(object, unconvert(value)); - } - - /** - * Gets the DynamoDB attribute type. - * @return The DynamoDB attribute type. - */ - public final DynamoDbAttributeType attributeType() { - return attributeType; - } - - /** - * Gets the key type. - * @return The key type if a key field, null otherwise. - */ - public final KeyType keyType() { - return properties.keyType(); - } - - /** - * Indicates if this attribute is a version attribute. - * @return True if it is, false otherwise. - */ - public final boolean versioned() { - return properties.versioned(); - } - - /** - * Gets the global secondary indexes. - * @param keyType The key type. - * @return The list of global secondary indexes. - */ - public final List globalSecondaryIndexNames(final KeyType keyType) { - if (properties.globalSecondaryIndexNames().containsKey(keyType)) { - return properties.globalSecondaryIndexNames().get(keyType); - } - return Collections.emptyList(); - } - - /** - * Gets the local secondary indexes. - * @return The list of local secondary indexes. - */ - public final List localSecondaryIndexNames() { - return properties.localSecondaryIndexNames(); - } - - /** - * Returns true if the field has any indexes. - * @return True if the propery matches. - */ - public final boolean indexed() { - return !properties.globalSecondaryIndexNames().isEmpty() || !properties.localSecondaryIndexNames().isEmpty(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#BEGINS_WITH - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition beginsWith(final V value) { - return Condition.builder().comparisonOperator(BEGINS_WITH).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified values. - * @param lo The start of the range (inclusive). - * @param hi The end of the range (inclusive). - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#BETWEEN - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition between(final V lo, final V hi) { - return Condition.builder().comparisonOperator(BETWEEN).attributeValueList(convert(lo), convert(hi)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#CONTAINS - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition contains(final V value) { - return Condition.builder().comparisonOperator(CONTAINS).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#EQ - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition eq(final V value) { - return Condition.builder().comparisonOperator(EQ).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#GE - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition ge(final V value) { - return Condition.builder().comparisonOperator(GE).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#GT - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition gt(final V value) { - return Condition.builder().comparisonOperator(GT).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified values. - * @param values The values. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#IN - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition in(final Collection values) { - return Condition.builder().comparisonOperator(IN).attributeValueList(LIST.convert(values, this)).build(); - } - - /** - * Creates a condition which filters on the specified values. - * @param values The values. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#IN - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition in(final V... values) { - return in(Arrays.asList(values)); - } - - /** - * Creates a condition which filters on the specified value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#NULL - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition isNull() { - return Condition.builder().comparisonOperator(NULL).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#LE - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition le(final V value) { - return Condition.builder().comparisonOperator(LE).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#LT - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition lt(final V value) { - return Condition.builder().comparisonOperator(LT).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#NE - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition ne(final V value) { - return Condition.builder().comparisonOperator(NE).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @param value The value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#NOT_CONTAINS - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition notContains(final V value) { - return Condition.builder().comparisonOperator(NOT_CONTAINS).attributeValueList(convert(value)).build(); - } - - /** - * Creates a condition which filters on the specified value. - * @return The condition. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#NOT_NULL - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition notNull() { - return Condition.builder().comparisonOperator(NOT_NULL).build(); - } - - /** - * Creates a condition which filters on any non-null argument; if {@code lo} - * is null a {@code LE} condition is applied on {@code hi}, if {@code hi} - * is null a {@code GE} condition is applied on {@code lo}. - * @param lo The start of the range (inclusive). - * @param hi The end of the range (inclusive). - * @return The condition or null if both arguments are null. - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#BETWEEN - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#EQ - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#GE - * @see software.amazon.awssdk.services.dynamodb.model.ComparisonOperator#LE - * @see software.amazon.awssdk.services.dynamodb.model.Condition - */ - public final Condition betweenAny(final V lo, final V hi) { - return lo == null ? (hi == null ? null : le(hi)) : (hi == null ? ge(lo) : (lo.equals(hi) ? eq(lo) : between(lo, hi))); - } - - public enum DynamoDbAttributeType { - B, N, S, BS, NS, SS, BOOL, NULL, L, M - } - - /** - * The field model properties. - */ - interface Properties { - String attributeName(); - - KeyType keyType(); - - boolean versioned(); - - Map> globalSecondaryIndexNames(); - - List localSecondaryIndexNames(); - - DynamoDbAutoGenerator autoGenerator(); - - final class Immutable implements Properties { - private final String attributeName; - private final KeyType keyType; - private final boolean versioned; - private final Map> globalSecondaryIndexNames; - private final List localSecondaryIndexNames; - private final DynamoDbAutoGenerator autoGenerator; - - Immutable(final Properties properties) { - this.attributeName = properties.attributeName(); - this.keyType = properties.keyType(); - this.versioned = properties.versioned(); - this.globalSecondaryIndexNames = properties.globalSecondaryIndexNames(); - this.localSecondaryIndexNames = properties.localSecondaryIndexNames(); - this.autoGenerator = properties.autoGenerator(); - } - - @Override - public String attributeName() { - return this.attributeName; - } - - @Override - public KeyType keyType() { - return this.keyType; - } - - @Override - public boolean versioned() { - return this.versioned; - } - - @Override - public Map> globalSecondaryIndexNames() { - return this.globalSecondaryIndexNames; - } - - @Override - public List localSecondaryIndexNames() { - return this.localSecondaryIndexNames; - } - - @Override - public DynamoDbAutoGenerator autoGenerator() { - return this.autoGenerator; - } - } - } - - /** - * Get/set reflection operations. - * @param The object type. - * @param The value type. - */ - interface Reflect { - V get(T object); - - void set(T object, V value); - } - - /** - * {@link DynamoDbMapperFieldModel} builder. - */ - static class Builder { - private final DynamoDbMapperFieldModel.Properties properties; - private DynamoDbTypeConverter converter; - private DynamoDbMapperFieldModel.Reflect reflect; - private DynamoDbAttributeType attributeType; - private Class targetType; - - Builder(Class targetType, DynamoDbMapperFieldModel.Properties properties) { - this.properties = properties; - this.targetType = targetType; - } - - public final Builder with(DynamoDbTypeConverter converter) { - this.converter = converter; - return this; - } - - public final Builder with(DynamoDbAttributeType attributeType) { - this.attributeType = attributeType; - return this; - } - - public final Builder with(DynamoDbMapperFieldModel.Reflect reflect) { - this.reflect = reflect; - return this; - } - - public final DynamoDbMapperFieldModel build() { - final DynamoDbMapperFieldModel result = new DynamoDbMapperFieldModel(this); - if ((result.keyType() != null || result.indexed()) && !result.attributeType().name().matches("[BNS]")) { - throw new DynamoDbMappingException(String.format( - "%s[%s]; only scalar (B, N, or S) type allowed for key", - targetType.getSimpleName(), result.name() - )); - } else if (result.keyType() != null && result.getGenerateStrategy() == ALWAYS) { - throw new DynamoDbMappingException(String.format( - "%s[%s]; auto-generated key and ALWAYS not allowed", - targetType.getSimpleName(), result.name() - )); - } - return result; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperModelFactory.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperModelFactory.java deleted file mode 100644 index d8a2b337bcb8..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperModelFactory.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * {@link DynamoDbMapper} table model factory. - */ -@SdkInternalApi -public interface DynamoDbMapperModelFactory { - - /** - * Gets/creates the mapper's model factory. - */ - TableFactory getTableFactory(DynamoDbMapperConfig config); - - /** - * {@link DynamoDbMapperModelFactory} factory. - */ - interface TableFactory { - /** - * Gets the table model for the given type and configuration. - */ - DynamoDbMapperTableModel getTable(Class clazz); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperTableModel.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperTableModel.java deleted file mode 100644 index cddf08f518f5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMapperTableModel.java +++ /dev/null @@ -1,505 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static software.amazon.awssdk.services.dynamodb.model.KeyType.HASH; -import static software.amazon.awssdk.services.dynamodb.model.KeyType.RANGE; -import static software.amazon.awssdk.services.dynamodb.model.ProjectionType.KEYS_ONLY; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.Projection; - -/** - * Table model. - * - * @param The object type. - */ -public final class DynamoDbMapperTableModel implements DynamoDbTypeConverter, T> { - - private final Map globalSecondaryIndexes; - private final Map localSecondaryIndexes; - private final Map> versions; - private final Map> fields; - private final Map> keys; - private final Class targetType; - - /** - * Constructs a new table model for the specified class. - * @param builder The builder. - */ - private DynamoDbMapperTableModel(final DynamoDbMapperTableModel.Builder builder) { - this.globalSecondaryIndexes = builder.globalSecondaryIndexes(); - this.localSecondaryIndexes = builder.localSecondaryIndexes(); - this.versions = builder.versions(); - this.fields = builder.fields(); - this.keys = builder.keys(); - this.targetType = builder.targetType; - } - - /** - * Gets the object type. - * @return The object type. - */ - public Class targetType() { - return this.targetType; - } - - /** - * Gets all the field models for the given class. - * @return The field models. - */ - public Collection> fields() { - return fields.values(); - } - - /** - * Gets the field model for a given attribute. - * @param The field model's value type. - * @param attributeName The attribute name. - * @return The field model. - */ - @SuppressWarnings("unchecked") - public DynamoDbMapperFieldModel field(final String attributeName) { - final DynamoDbMapperFieldModel field = (DynamoDbMapperFieldModel) fields.get(attributeName); - if (field == null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + attributeName + "]; no mapping for attribute by name" - ); - } - return field; - } - - /** - * Gets all the key field models for the given class. - * @return The field models. - */ - public Collection> keys() { - return keys.values(); - } - - /** - * Gets the hash key field model for the specified type. - * @param The hash key type. - * @return The hash key field model. - * @throws DynamoDbMappingException If the hash key is not present. - */ - @SuppressWarnings("unchecked") - public DynamoDbMapperFieldModel hashKey() { - final DynamoDbMapperFieldModel field = (DynamoDbMapperFieldModel) keys.get(HASH); - if (field == null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "; no mapping for HASH key" - ); - } - return field; - } - - /** - * Gets the range key field model for the specified type. - * @param The range key type. - * @return The range key field model. - * @throws DynamoDbMappingException If the range key is not present. - */ - @SuppressWarnings("unchecked") - public DynamoDbMapperFieldModel rangeKey() { - final DynamoDbMapperFieldModel field = (DynamoDbMapperFieldModel) keys.get(RANGE); - if (field == null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "; no mapping for RANGE key" - ); - } - return field; - } - - /** - * Gets the range key field model for the specified type. - * @param The range key type. - * @return The range key field model, or null if not present. - */ - @SuppressWarnings("unchecked") - public DynamoDbMapperFieldModel rangeKeyIfExists() { - return (DynamoDbMapperFieldModel) keys.get(RANGE); - } - - /** - * Gets all the version fields for the given class. - * @return The field models. - */ - public Collection> versions() { - return versions.values(); - } - - /** - * Indicates if this table has any versioned attributes. - * @return True if any versioned attributes, false otherwise. - */ - public boolean versioned() { - return !versions.isEmpty(); - } - - /** - * Gets the global secondary indexes for the given class. - * @return The map of index name to GlobalSecondaryIndexes. - */ - public Collection globalSecondaryIndexes() { - if (globalSecondaryIndexes.isEmpty()) { - return null; - } - final Collection copies = new ArrayList(globalSecondaryIndexes.size()); - for (final String indexName : globalSecondaryIndexes.keySet()) { - copies.add(globalSecondaryIndex(indexName)); - } - return copies; - } - - /** - * Gets the global secondary index. - * @param indexName The index name. - * @return The global secondary index or null. - */ - public GlobalSecondaryIndex globalSecondaryIndex(final String indexName) { - if (!globalSecondaryIndexes.containsKey(indexName)) { - return null; - } - final GlobalSecondaryIndex gsi = globalSecondaryIndexes.get(indexName); - final GlobalSecondaryIndex.Builder copyBuilder = GlobalSecondaryIndex.builder() - .indexName(gsi.indexName()) - .projection(Projection.builder() - .projectionType(gsi.projection().projectionType()) - .build()); - List keySchemas = new ArrayList<>(); - for (final KeySchemaElement key : gsi.keySchema()) { - keySchemas.add(KeySchemaElement.builder().attributeName(key.attributeName()).keyType(key.keyType()).build()); - } - copyBuilder.keySchema(keySchemas); - return copyBuilder.build(); - } - - /** - * Gets the local secondary indexes for the given class. - * @return The map of index name to LocalSecondaryIndexes. - */ - public Collection localSecondaryIndexes() { - if (localSecondaryIndexes.isEmpty()) { - return null; - } - final Collection copies = new ArrayList(localSecondaryIndexes.size()); - for (final String indexName : localSecondaryIndexes.keySet()) { - copies.add(localSecondaryIndex(indexName)); - } - return copies; - } - - /** - * Gets the local secondary index by name. - * @param indexName The index name. - * @return The local secondary index, or null. - */ - public LocalSecondaryIndex localSecondaryIndex(final String indexName) { - if (!localSecondaryIndexes.containsKey(indexName)) { - return null; - } - final LocalSecondaryIndex lsi = localSecondaryIndexes.get(indexName); - final LocalSecondaryIndex.Builder copyBuilder = LocalSecondaryIndex.builder() - .indexName(lsi.indexName()) - .projection(Projection.builder() - .projectionType(lsi.projection().projectionType()) - .build()); - - List keySchemas = new ArrayList<>(); - for (final KeySchemaElement key : lsi.keySchema()) { - keySchemas.add(KeySchemaElement.builder() - .attributeName(key.attributeName()) - .keyType(key.keyType()) - .build()); - } - copyBuilder.keySchema(keySchemas); - return copyBuilder.build(); - } - - /** - * {@inheritDoc} - */ - @Override - public Map convert(final T object) { - final Map map = new LinkedHashMap(); - for (final DynamoDbMapperFieldModel field : fields()) { - try { - final AttributeValue value = field.getAndConvert(object); - if (value != null) { - map.put(field.name(), value); - } - } catch (final RuntimeException e) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + field.name() + "]; could not convert attribute", e - ); - } - } - return map; - } - - /** - * {@inheritDoc} - */ - @Override - public T unconvert(final Map object) { - final T result = StandardBeanProperties.DeclaringReflect.newInstance(targetType); - if (!object.isEmpty()) { - for (final DynamoDbMapperFieldModel field : fields()) { - try { - final AttributeValue value = object.get(field.name()); - if (value != null) { - field.unconvertAndSet(result, value); - } - } catch (final RuntimeException e) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + field.name() + "]; could not unconvert attribute", e - ); - } - } - } - return result; - } - - /** - * Creates a new object instance with the keys populated. - * @param The hash key type. - * @param The range key type. - * @param hashKey The hash key. - * @param rangeKey The range key (optional if not present on table). - * @return The new instance. - */ - public T createKey(final H hashKey, final R rangeKey) { - final T key = StandardBeanProperties.DeclaringReflect.newInstance(targetType); - if (hashKey != null) { - final DynamoDbMapperFieldModel hk = hashKey(); - hk.set(key, hashKey); - } - if (rangeKey != null) { - final DynamoDbMapperFieldModel rk = rangeKey(); - rk.set(key, rangeKey); - } - return key; - } - - /** - * Creates a new key map from the specified object. - * @param The hash key type. - * @param The range key type. - * @return The key map. - */ - public Map convertKey(final T key) { - final DynamoDbMapperFieldModel hk = this.hashKey(); - final DynamoDbMapperFieldModel rk = this.rangeKeyIfExists(); - return this.convertKey(hk.get(key), (rk == null ? (R) null : rk.get(key))); - } - - /** - * Creates a new key map from the specified hash and range key. - * @param The hash key type. - * @param The range key type. - * @param hashKey The hash key. - * @param rangeKey The range key (optional if not present on table). - * @return The key map. - */ - public Map convertKey(final H hashKey, final R rangeKey) { - final Map key = new LinkedHashMap(4); - final DynamoDbMapperFieldModel hk = this.hashKey(); - final AttributeValue hkValue = hashKey == null ? null : hk.convert(hashKey); - if (hkValue != null) { - key.put(hk.name(), hkValue); - } else { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + hk.name() + "]; no HASH key value present" - ); - } - final DynamoDbMapperFieldModel rk = this.rangeKeyIfExists(); - final AttributeValue rkValue = rangeKey == null ? null : rk.convert(rangeKey); - if (rkValue != null) { - key.put(rk.name(), rkValue); - } else if (rk != null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + rk.name() + "]; no RANGE key value present" - ); - } - return key; - } - - /** - * The table model properties. - */ - interface Properties { - String tableName(); - - final class Immutable implements Properties { - private final String tableName; - - Immutable(final Properties properties) { - this.tableName = properties.tableName(); - } - - @Override - public String tableName() { - return this.tableName; - } - } - } - - /** - * {@link DynamoDbMapperTableModel} builder. - */ - static class Builder { - private final Map> versions; - private final Map> fields; - private final Map> keys; - private final Properties properties; - private final Class targetType; - - Builder(Class targetType, Properties properties) { - this.versions = new LinkedHashMap>(4); - this.fields = new LinkedHashMap>(); - this.keys = new EnumMap>(KeyType.class); - this.properties = properties; - this.targetType = targetType; - } - - public Builder with(final DynamoDbMapperFieldModel field) { - fields.put(field.name(), field); - if (field.keyType() != null) { - keys.put(field.keyType(), field); - } - if (field.versioned()) { - versions.put(field.name(), field); - } - return this; - } - - public Map globalSecondaryIndexes() { - final Map map = new LinkedHashMap(); - for (final DynamoDbMapperFieldModel field : fields.values()) { - for (final String indexName : field.globalSecondaryIndexNames(HASH)) { - final GlobalSecondaryIndex.Builder gsiBuilder = GlobalSecondaryIndex.builder() - .indexName(indexName) - .projection(Projection.builder() - .projectionType(KEYS_ONLY) - .build()) - .keySchema(KeySchemaElement.builder() - .attributeName(field.name()) - .keyType(HASH).build()); - if (map.put(indexName, gsiBuilder.build()) != null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + field.name() + "]; must not duplicate GSI " + indexName - ); - } - } - } - for (final DynamoDbMapperFieldModel field : fields.values()) { - for (final String indexName : field.globalSecondaryIndexNames(RANGE)) { - final GlobalSecondaryIndex gsi = map.get(indexName); - if (gsi == null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + field.name() + "]; no HASH key for GSI " + indexName - ); - } - - List keySchemas = new ArrayList<>(); - keySchemas.addAll(gsi.keySchema()); - - keySchemas.add(KeySchemaElement.builder() - .attributeName(field.name()) - .keyType(RANGE).build()); - - map.put(indexName, - gsi.toBuilder() - .keySchema(keySchemas).build()); - } - } - if (map.isEmpty()) { - return Collections.emptyMap(); - } - return Collections.unmodifiableMap(map); - } - - public Map localSecondaryIndexes() { - final Map map = new LinkedHashMap(); - for (final DynamoDbMapperFieldModel field : fields.values()) { - for (final String indexName : field.localSecondaryIndexNames()) { - final LocalSecondaryIndex.Builder lsiBuilder = LocalSecondaryIndex.builder() - .indexName(indexName) - .projection(Projection.builder() - .projectionType(KEYS_ONLY) - .build()) - .keySchema(KeySchemaElement.builder() - .attributeName(keys.get(HASH).name()) - .keyType(HASH).build(), - KeySchemaElement.builder() - .attributeName(field.name()) - .keyType(RANGE).build()); - if (map.put(indexName, lsiBuilder.build()) != null) { - throw new DynamoDbMappingException( - targetType.getSimpleName() + "[" + field.name() + "]; must not duplicate LSI " + indexName - ); - } - } - } - if (map.isEmpty()) { - return Collections.emptyMap(); - } - return Collections.unmodifiableMap(map); - } - - private Map> versions() { - if (versions.isEmpty()) { - return Collections.>emptyMap(); - } - return Collections.unmodifiableMap(versions); - } - - public Map> fields() { - if (fields.isEmpty()) { - return Collections.>emptyMap(); - } - return Collections.unmodifiableMap(fields); - } - - public Map> keys() { - if (keys.isEmpty()) { - return Collections.>emptyMap(); - } - return Collections.unmodifiableMap(keys); - } - - public DynamoDbMapperTableModel build() { - final DynamoDbMapperTableModel result = new DynamoDbMapperTableModel(this); - if (properties.tableName() != null) { - result.hashKey(); //<- make sure the hash key is present - } - return result; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMappingException.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMappingException.java deleted file mode 100644 index 054e93a5a54c..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMappingException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -/** - * Generic exception for problems occurring when mapping DynamoDB items to Java - * objects or vice versa. Excludes service exceptions. - */ -public class DynamoDbMappingException extends RuntimeException { - - private static final long serialVersionUID = -4883173289978517967L; - - public DynamoDbMappingException() { - super(); - } - - public DynamoDbMappingException(String message, Throwable cause) { - super(message, cause); - } - - public DynamoDbMappingException(String message) { - super(message); - } - - public DynamoDbMappingException(Throwable cause) { - super(cause); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMappingsRegistry.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMappingsRegistry.java deleted file mode 100644 index bdf8b3b31c55..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMappingsRegistry.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardBeanProperties.Bean; - -/** - * Reflection assistant for {@link DynamoDbMapper} - * - * @deprecated Replaced by {@link StandardBeanProperties}/{@link StandardModelFactories} - */ -@Deprecated -@SdkInternalApi -final class DynamoDbMappingsRegistry { - - /** - * The default instance. - */ - private static final DynamoDbMappingsRegistry INSTANCE = new DynamoDbMappingsRegistry(); - /** - * The cache of class to mapping definition. - */ - private final ConcurrentMap, Mappings> mappings = new ConcurrentHashMap, Mappings>(); - - /** - * Gets the default instance. - * @return The default instance. - */ - static DynamoDbMappingsRegistry instance() { - return INSTANCE; - } - - /** - * Gets the mapping definition for a given class. - * @param clazz The class. - * @return The mapping definition. - */ - Mappings mappingsOf(final Class clazz) { - if (!mappings.containsKey(clazz)) { - mappings.putIfAbsent(clazz, new Mappings(clazz)); - } - return mappings.get(clazz); - } - - /** - * Holds the properties for mapping an object. - */ - static final class Mappings { - private final Map byNames = new HashMap(); - - private Mappings(final Class clazz) { - for (final Map.Entry> bean : - StandardBeanProperties.of((Class) clazz).map().entrySet()) { - final Mapping mapping = new Mapping(bean.getValue()); - byNames.put(mapping.getAttributeName(), mapping); - } - } - - Collection mappings() { - return byNames.values(); - } - } - - /** - * Holds the properties for mapping an object attribute. - */ - static final class Mapping { - private final Bean bean; - - private Mapping(final Bean bean) { - this.bean = bean; - } - - Method getter() { - return bean.type().getter(); - } - - boolean isPrimaryKey() { - return bean.properties().keyType() != null; - } - - boolean isVersion() { - return bean.properties().versioned(); - } - - String getAttributeName() { - return bean.properties().attributeName(); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMarshaller.java deleted file mode 100644 index 20251661db39..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMarshaller.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -/** - * Marshaller interface for storing complex types in DynamoDB as Strings. - * Implementors provide methods to transform instances of a class to and from - * Strings. - * - * @deprecated Replaced by {@link DynamoDbTypeConverter} - * - *

    A {@link DynamoDbTypeConverted} with {@link String} as source would - * perform the same conversion. Please consider, if your marshaller is thread - * safe before replacing. In the new implementation, a single instance of - * {@link DynamoDbTypeConverted} is created per field/attribute. In the old, - * an new instance of the marshaller was created for each call to - * {@code marshall} and {@code unmarshall}. If your marshaller/converter is not - * thread safe, it is recommended to specify a converter which will instantiate - * a new marshaller per call.

    - * - *
    - * public class CustomConverter<T> implements DynamoDBTypeConverter<String,T> {
    - *     @Override
    - *     public final String convert(final T object) {
    - *         return ...
    - *     }
    - *     @Override
    - *     public final T unconvert(final String object) {
    - *         return ...
    - *     }
    - * }
    - * 
    - */ -@Deprecated -public interface DynamoDbMarshaller { - - /** - * Turns an object of type T into its String representation. - */ - String marshall(T getterReturnResult); - - /** - * Turns a String representation of an object of type T into an object. - */ - T unmarshall(Class clazz, String obj); -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMarshalling.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMarshalling.java deleted file mode 100644 index 7480280a6218..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbMarshalling.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Set; - -/** - * Annotation to mark a property as using a custom marshaller. This is required - * when storing anything other than {@link String}s, {@link Number}s, and - * {@link Set}s of the same to DynamoDB. Any object that can be converted into a - * String representation and vice versa can be saved in this manner. This - * annotation can be applied to either the getter method or the class field for - * the specific property. If the annotation is applied directly to the class - * field, the corresponding getter and setter must be declared in the same - * class. - * - * @see DynamoDbMarshaller - * @see JsonMarshaller - * - * @deprecated Replaced by {@link DynamoDbTypeConverted} - * - *

    A {@link DynamoDbTypeConverted} with {@link String} as source would - * perform the same conversion. Please consider, if your marshaller is thread - * safe before replacing. In the new implementation, a single instance of - * {@link DynamoDbTypeConverted} is created per field/attribute. In the old, - * an new instance of the marshaller was created for each call to - * {@code marshall} and {@code unmarshall}. If your marshaller/converter is not - * thread safe, it is recommended to specify a converter which will instantiate - * a new marshaller per call.

    - * - *
    - * public class CustomConverter<T> implements DynamoDBTypeConverter<String,T> {
    - *     @Override
    - *     public final String convert(final T object) {
    - *         return ...
    - *     }
    - *     @Override
    - *     public final T unconvert(final String object) {
    - *         return ...
    - *     }
    - * }
    - * 
    - */ -@Deprecated -@DynamoDb -@DynamoDbTypeConverted(converter = DynamoDbMarshalling.Converter.class) -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.S) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbMarshalling { - - /** - * The class of the Marshaller that converts this property to and from a - * String. - */ - Class> marshallerClass(); - - /** - * Marshalling type converter. - */ - final class Converter implements DynamoDbTypeConverter { - private final Class> marshallerClass; - private final Class targetType; - - Converter(final Class targetType, final DynamoDbMarshalling annotation) { - this.marshallerClass = (Class>) annotation.marshallerClass(); - this.targetType = targetType; - } - - @Override - public String convert(final T object) { - return marshaller().marshall(object); - } - - @Override - public T unconvert(final String object) { - return marshaller().unmarshall(targetType, object); - } - - private DynamoDbMarshaller marshaller() { - try { - return marshallerClass.newInstance(); - } catch (final Exception e) { - throw new DynamoDbMappingException("Unable to instantiate marshaller " + marshallerClass, e); - } - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbNamed.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbNamed.java deleted file mode 100644 index 31ad11254c47..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbNamed.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for overriding a property's DynamoDB attribute name. - * - *
    - * @DynamoDBNamed("InternalStatus")
    - * public String status()
    - * 
    - * - *

    This annotation has the lowest precedence among other property/field - * annotations where {@code attributeName} may be specified.

    - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbNamed { - - /** - * Use when the name of the attribute as stored in DynamoDB should differ - * from the name used by the getter / setter. - */ - String value(); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbNativeBoolean.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbNativeBoolean.java deleted file mode 100644 index 3a3aaa330354..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbNativeBoolean.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * An annotation that marks a {@code boolean} or {@code Boolean} attribute - * of a modeled class which should be serialized as a DynamoDB BOOL. For - * backwards compatibility with old versions of the {@code DynamoDBMapper}, - * by default booleans are serialized using the DynamoDB N type, with a value - * of '1' representing 'true' and a value of '0' representing 'false'. - *

    - * Using this annotation on the field definition or getter method definition - * for the attribute will cause it to be serialized as DynamoDB-native BOOL - * type. Old versions of the {@code DynamoDBMapper} which do not know about the - * BOOL type will be unable to read items containing BOOLs, so don't use me - * unless all readers of your table are using an updated version of the mapper. - * - * @deprecated - Replaced by {@link DynamoDbTyped} - */ -@Deprecated -@DynamoDb -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.BOOL) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbNativeBoolean { -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbQueryExpression.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbQueryExpression.java deleted file mode 100644 index d67fa1f1c71a..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbQueryExpression.java +++ /dev/null @@ -1,1325 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.Select; - -/** - * A query expression. - */ -public class DynamoDbQueryExpression { - - private boolean consistentRead = true; - private boolean scanIndexForward = true; - private T hashKeyValues; - private Map rangeKeyConditions; - private Map exclusiveStartKey; - private Integer limit; - private String indexName; - private Map queryFilter; - private String conditionalOperator; - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - */ - private String filterExpression; - - /** - * The condition that specifies the key value(s) for items to be retrieved - * by the Query action. - */ - private String keyConditionExpression; - - /** - * One or more substitution variables for simplifying complex expressions. - */ - private Map expressionAttributeNames; - - /** - * One or more values that can be substituted in an expression. - */ - private Map expressionAttributeValues; - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index.

    • ALL_ATTRIBUTES - Returns all of - * the item attributes from the specified table or index. If you query a - * local secondary index, then for each matching item in the index - * DynamoDB will fetch the entire item from the parent table. If the - * index is configured to project all item attributes, then all of the - * data can be obtained from the local secondary index, and no fetching - * is required.

    • ALL_PROJECTED_ATTRIBUTES - - * Allowed only when querying an index. Retrieves all attributes that - * have been projected into the index. If the index is configured to - * project all attributes, this return value is equivalent to specifying - * ALL_ATTRIBUTES.

    • COUNT - - * Returns the number of matching items, rather than the matching items - * themselves.

    • SPECIFIC_ATTRIBUTES - Returns - * only the attributes listed in AttributesToGet. This return - * value is equivalent to specifying AttributesToGet without - * specifying any value for Select.

      If you query a local - * secondary index and request only attributes that are projected into - * that index, the operation will read only the index and not the table. - * If any of the requested attributes are not projected into the local - * secondary index, DynamoDB will fetch each of these attributes from the - * parent table. This extra fetching incurs additional throughput cost - * and latency.

      If you query a global secondary index, you can only - * request attributes that are projected into the index. Global secondary - * index queries cannot fetch attributes from the parent table.

    • - *

    If neither Select nor AttributesToGet are - * specified, DynamoDB defaults to ALL_ATTRIBUTES when - * accessing a table, and ALL_PROJECTED_ATTRIBUTES when - * accessing an index. You cannot use both Select and - * AttributesToGet together in a single request, unless the value - * for Select is SPECIFIC_ATTRIBUTES. (This usage is - * equivalent to specifying AttributesToGet without any value for - * Select.) - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - */ - private String select; - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - */ - private String projectionExpression; - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the query. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.queryPage}, not {@code DynamoDBMapper.query}. - */ - private String returnConsumedCapacity; - - /** - * Returns whether this query uses consistent reads. - */ - public boolean isConsistentRead() { - return consistentRead; - } - - /** - * Sets whether this query uses consistent reads. - */ - public void setConsistentRead(boolean consistentRead) { - this.consistentRead = consistentRead; - } - - /** - * Sets whether this query uses consistent reads and returns a pointer to - * this object for method-chaining. - */ - public DynamoDbQueryExpression withConsistentRead(boolean consistentRead) { - this.consistentRead = consistentRead; - return this; - } - - /** - * Returns whether this query scans forward. - */ - public boolean isScanIndexForward() { - return scanIndexForward; - } - - /** - * Sets whether this query scans forward. - */ - public void setScanIndexForward(boolean scanIndexForward) { - this.scanIndexForward = scanIndexForward; - } - - /** - * Sets whether this query scans forward and returns a pointer to this - * object for method-chaining. - */ - public DynamoDbQueryExpression withScanIndexForward(boolean scanIndexForward) { - this.scanIndexForward = scanIndexForward; - return this; - } - - /** - * Returns the exclusive start key for this query. - */ - public Map getExclusiveStartKey() { - return exclusiveStartKey; - } - - /** - * Sets the exclusive start key for this query. - */ - public void setExclusiveStartKey(Map exclusiveStartKey) { - this.exclusiveStartKey = exclusiveStartKey; - } - - /** - * Sets the exclusive start key for this query and returns a pointer to this - * object for method-chaining. - */ - public DynamoDbQueryExpression withExclusiveStartKey(Map exclusiveStartKey) { - this.exclusiveStartKey = exclusiveStartKey; - return this; - } - - /** - * Returns the maximum number of items to retrieve in each service request - * to DynamoDB. - *

    - * Note that when calling {@code DynamoDBMapper.query}, multiple requests - * are made to DynamoDB if needed to retrieve the entire result set. Setting - * this will limit the number of items retrieved by each request, NOT - * the total number of results that will be retrieved. Use - * {@code DynamoDBMapper.queryPage} to retrieve a single page of items from - * DynamoDB. - */ - public Integer limit() { - return limit; - } - - /** - * Sets the maximum number of items to retrieve in each service request to - * DynamoDB. - *

    - * Note that when calling {@code DynamoDBMapper.query}, multiple requests - * are made to DynamoDB if needed to retrieve the entire result set. Setting - * this will limit the number of items retrieved by each request, NOT - * the total number of results that will be retrieved. Use - * {@code DynamoDBMapper.queryPage} to retrieve a single page of items from - * DynamoDB. - */ - public void setLimit(Integer limit) { - this.limit = limit; - } - - /** - * Sets the maximum number of items to retrieve in each service request to - * DynamoDB and returns a pointer to this object for method-chaining. - *

    - * Note that when calling {@code DynamoDBMapper.query}, multiple requests - * are made to DynamoDB if needed to retrieve the entire result set. Setting - * this will limit the number of items retrieved by each request, NOT - * the total number of results that will be retrieved. Use - * {@code DynamoDBMapper.queryPage} to retrieve a single page of items from - * DynamoDB. - */ - public DynamoDbQueryExpression withLimit(Integer limit) { - this.limit = limit; - return this; - } - - /** - * Gets the hash key value(s) for this query. All hash key attributes for - * the table must be specified with this key object. - */ - public T getHashKeyValues() { - return hashKeyValues; - } - - - /** - * Sets the hash key value(s) for this query. All hash key attributes for - * the table must be specified with this key object. - * - * Note 1: Currently the DynamoDBMapper supports only one value per hash key. - * Note 2: Currently the Amazon DynamoDB supports only one hash key per - * table/index. - */ - public void setHashKeyValues(T hashKeyValues) { - this.hashKeyValues = hashKeyValues; - } - - /** - * Sets the hash key value(s) for this query. All hash key attributes for - * the table must be specified with this key object. - */ - public DynamoDbQueryExpression withHashKeyValues(T hashKObject) { - setHashKeyValues(hashKObject); - return this; - } - - /** - * Gets the range key condition for this query. All range key attributes for - * the table must be specified by attribute name in the map. - */ - public Map getRangeKeyConditions() { - return rangeKeyConditions; - } - - /** - * Sets the range key condition for this query. All range key attributes for - * the table must be specified by attribute name in the map. - * - * @param rangeKeyConditions a map from key name to condition - * NOTE: The current DynamoDB service only allows up to one - * range key condition per query. Providing more than one - * range key condition will result in a SdkClientException. - */ - public void setRangeKeyConditions(Map rangeKeyConditions) { - this.rangeKeyConditions = rangeKeyConditions; - } - - /** - * Sets the range key condition for this query. All range key attributes for - * the table must be specified by attribute name in the map. - * - * @param rangeKeyConditions a map from key name to condition - * NOTE: The current DynamoDB service only allows up to one range - * key condition per query. Providing more than one range key - * condition will result in a SdkClientException. - */ - public DynamoDbQueryExpression withRangeKeyConditions(Map rangeKeyConditions) { - setRangeKeyConditions(rangeKeyConditions); - return this; - } - - /** - * Sets one range key condition for this query, using the attribute name of - * the range key. All range key attributes for the table must be specified - * by using {@link DynamoDbRangeKey} or {@link DynamoDbIndexRangeKey} annotations - * before executing the query. - *

    - *
    If the attribute is the primary range key
    - *
    users should NOT set any index name for this query.
    - *
    If the attribute is an index range key
    - *
    - * {@link DynamoDbMapper} will automatically set the index name if the - * range key is annotated as only used by one local secondary index, - * otherwise users must set the index name manually by either - * {@link DynamoDbQueryExpression#setIndexName(String)} or - * {@link DynamoDbQueryExpression#withIndexName(String)}. - *
    - *
    - * - * @param rangeKeyAttributeName - * This can be either the primary range key of the table or an - * index range key. - * - * @param rangeKeyCondition - * Condition specified on the given range key for this query. - */ - public DynamoDbQueryExpression withRangeKeyCondition(String rangeKeyAttributeName, Condition rangeKeyCondition) { - if (rangeKeyConditions == null) { - rangeKeyConditions = new HashMap(); - } - rangeKeyConditions.put(rangeKeyAttributeName, rangeKeyCondition); - return this; - } - - /** - * Returns the name of the index to be used by this query. - */ - public String getIndexName() { - return indexName; - } - - /** - * Sets the name of the index to be used by this query. The hash key - * and/or range key of the index must be specified by adding - * {@link DynamoDbIndexHashKey} or {@code DynamoDBIndexRangeKey} - * annotations to the appropriate getter methods of the mapped - * object. - */ - public void setIndexName(String indexName) { - this.indexName = indexName; - } - - /** - * Sets the name of the index to be used by this query. The hash key - * and/or range key of the index must be specified by adding - * {@link DynamoDbIndexHashKey} or {@code DynamoDBIndexRangeKey} - * annotations to the appropriate getter methods of the mapped - * object. - *

    - * Returns a pointer to this object for method-chaining. - */ - public DynamoDbQueryExpression withIndexName(String indexName) { - setIndexName(indexName); - return this; - } - - /** - * Returns the query filter applied on this query. - */ - public Map getQueryFilter() { - return queryFilter; - } - - /** - * Sets the query filter applied on this query. - */ - public void setQueryFilter(Map queryFilter) { - this.queryFilter = queryFilter; - } - - /** - * Sets the query filter applied on this query. - *

    Returns a pointer to this object for method-chaining. - */ - public DynamoDbQueryExpression withQueryFilter(Map queryFilter) { - setQueryFilter(queryFilter); - return this; - } - - /** - * Adds a new condition to the the query filter. - *

    Returns a pointer to this object for method-chaining. - * - * @param attributeName - * The name of the attribute on which the specified condition - * operates. - * @param condition - * The filter condition applied on the attribute. - */ - public DynamoDbQueryExpression withQueryFilterEntry(String attributeName, Condition condition) { - if (queryFilter == null) { - queryFilter = new HashMap(); - } - queryFilter.put(attributeName, condition); - return this; - } - - /** - * Returns the logical operator on the query filter conditions. - */ - public String getConditionalOperator() { - return conditionalOperator; - } - - /** - * Sets the logical operator on the query filter conditions. - */ - public void setConditionalOperator(String conditionalOperator) { - this.conditionalOperator = conditionalOperator; - } - - /** - * Sets the logical operator on the query filter conditions. - */ - public void setConditionalOperator(ConditionalOperator conditionalOperator) { - this.conditionalOperator = conditionalOperator.toString(); - } - - /** - * Sets the logical operator on the query filter conditions. - *

    Returns a pointer to this object for method-chaining. - */ - public DynamoDbQueryExpression withConditionalOperator(String conditionalOperator) { - setConditionalOperator(conditionalOperator); - return this; - } - - /** - * Sets the logical operator on the query filter conditions. - *

    Returns a pointer to this object for method-chaining. - */ - public DynamoDbQueryExpression withConditionalOperator(ConditionalOperator conditionalOperator) { - setConditionalOperator(conditionalOperator); - return this; - } - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - * - * @return Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any - * items that do not match the expression are not returned. - * @see QueryRequest#getFilterExpression() - */ - public String getFilterExpression() { - return filterExpression; - } - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - * - * @param filterExpression - * Evaluates the query results and returns only the desired - * values. - *

    - * The condition you specify is applied to the items queried; any - * items that do not match the expression are not returned. - * @see QueryRequest#setFilterExpression(String) - */ - public void setFilterExpression(String filterExpression) { - this.filterExpression = filterExpression; - } - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - * - * @param filterExpression - * Evaluates the query results and returns only the desired - * values. - *

    - * The condition you specify is applied to the items queried; any - * items that do not match the expression are not returned. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see QueryRequest#withFilterExpression(String) - */ - public DynamoDbQueryExpression withFilterExpression( - String filterExpression) { - this.filterExpression = filterExpression; - return this; - } - - /** - * Returns the condition that specifies the key value(s) for items to be - * retrieved by the Query action. - *

    - * The condition must perform an equality test on a single hash key value. - * The condition can also test for one or more range key values. A - * Query can use KeyConditionExpression to retrieve a single - * item with a given hash and range key value, or several items that have - * the same hash key value but different range key values. - *

    - * The hash key equality test is required, and must be specified in the - * following format: - *

    - * hashAttributeName = :hashval - *

    - * If you also want to provide a range key condition, it must be combined - * using AND with the hash key condition. Following is an example, - * using the = comparison operator for the range key: - *

    - * hashAttributeName = :hashval AND - * rangeAttributeName = :rangeval - *

    - * Valid comparisons for the range key condition are as follows: - *

      - *
    • - *

      - * rangeAttributeName = :rangeval - true if - * the range key is equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName < :rangeval - true if - * the range key is less than :rangeval.

    • - *
    • - *

      - * rangeAttributeName <= :rangeval - true - * if the range key is less than or equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName > :rangeval - true if - * the range key is greater than :rangeval.

    • - *
    • - *

      - * rangeAttributeName >=:rangeval - true - * if the range key is greater than or equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName BETWEEN :rangeval1 - * AND :rangeval2 - true if the range key is less than - * or greater than :rangeval1, and less than or equal to - * :rangeval2.

    • - *
    • - *

      - * begins_with (rangeAttributeName, - * :rangeval) - true if the range key begins with a - * particular operand. Note that the function name begins_with - * is case-sensitive.

    • - *
    - *

    - * Use ExpressionAttributeValues (via {@link #withExpressionAttributeValues(Map)}) to - * replace tokens such as :hashval and :rangeval - * with actual values at runtime. - *

    - * You can optionally use ExpressionAttributeNames (via - * {@link #withExpressionAttributeNames(Map)}) to replace the names of the hash and range - * attributes with placeholder tokens. This might be necessary if an - * attribute name conflicts with a DynamoDB reserved word. For example, the - * following KeyConditionExpression causes an error because - * Size is a reserved word: - *

      - *
    • Size = :myval
    • - *
    - *

    - * To work around this, define a placeholder (such a #myval) to - * represent the attribute name Size. KeyConditionExpression - * then is as follows: - *

      - *
    • #S = - * :myval
    • - *
    - *

    - * For a list of reserved words, see Reserved Words in the Amazon DynamoDB Developer Guide. - *

    - * For more information on ExpressionAttributeNames and - * ExpressionAttributeValues, see Using Placeholders for Attribute Names and Values in the Amazon - * DynamoDB Developer Guide. - *

    - * KeyConditionExpression replaces the legacy KeyConditions - * parameter. - * - * @return The condition that specifies the key value(s) for items to be - * retrieved by the Query action. - *

    - * The condition must perform an equality test on a single hash key - * value. The condition can also test for one or more range key - * values. A Query can use KeyConditionExpression to - * retrieve a single item with a given hash and range key value, or - * several items that have the same hash key value but different - * range key values. - *

    - * The hash key equality test is required, and must be specified in - * the following format: - *

    - * hashAttributeName = :hashval - *

    - * If you also want to provide a range key condition, it must be - * combined using AND with the hash key condition. Following - * is an example, using the = comparison operator for the - * range key: - *

    - * hashAttributeName = :hashval - * AND rangeAttributeName = - * :rangeval - *

    - * Valid comparisons for the range key condition are as follows: - *

      - *
    • - *

      - * rangeAttributeName = :rangeval - - * true if the range key is equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName < :rangeval - - * true if the range key is less than :rangeval.

    • - *
    • - *

      - * rangeAttributeName <= :rangeval - * - true if the range key is less than or equal to - * :rangeval.

    • - *
    • - *

      - * rangeAttributeName > :rangeval - - * true if the range key is greater than :rangeval.

    • - *
    • - *

      - * rangeAttributeName >= :rangeval - * - true if the range key is greater than or equal to - * :rangeval.

    • - *
    • - *

      - * rangeAttributeName BETWEEN - * :rangeval1 AND :rangeval2 - true - * if the range key is less than or greater than - * :rangeval1, and less than or equal to - * :rangeval2.

    • - *
    • - *

      - * begins_with (rangeAttributeName, - * :rangeval) - true if the range key begins - * with a particular operand. Note that the function name - * begins_with is case-sensitive.

    • - *
    - *

    - * Use ExpressionAttributeValues (via - * {@link #withExpressionAttributeValues(Map)}) to replace tokens such as - * :hashval and :rangeval with actual - * values at runtime. - *

    - * You can optionally use ExpressionAttributeNames (via - * {@link #withExpressionAttributeNames(Map)}) to replace the names of the hash and - * range attributes with placeholder tokens. This might be necessary - * if an attribute name conflicts with a DynamoDB reserved word. For - * example, the following KeyConditionExpression causes an - * error because Size is a reserved word: - *

      - *
    • Size = :myval
    • - *
    - *

    - * To work around this, define a placeholder (such a - * #myval) to represent the attribute name Size. - * KeyConditionExpression then is as follows: - *

      - *
    • #S = - * :myval
    • - *
    - *

    - * For a list of reserved words, see Reserved Words in the Amazon DynamoDB Developer - * Guide. - *

    - * For more information on ExpressionAttributeNames and - * ExpressionAttributeValues, see Using Placeholders for Attribute Names and Values in the - * Amazon DynamoDB Developer Guide. - *

    - * KeyConditionExpression replaces the legacy - * KeyConditions parameter. - */ - public String getKeyConditionExpression() { - return keyConditionExpression; - } - - /** - * Sets the condition that specifies the key value(s) for items to be - * retrieved by the Query action. - *

    - * The condition must perform an equality test on a single hash key value. - * The condition can also test for one or more range key values. A - * Query can use KeyConditionExpression to retrieve a single - * item with a given hash and range key value, or several items that have - * the same hash key value but different range key values. - *

    - * The hash key equality test is required, and must be specified in the - * following format: - *

    - * hashAttributeName = :hashval - *

    - * If you also want to provide a range key condition, it must be combined - * using AND with the hash key condition. Following is an example, - * using the = comparison operator for the range key: - *

    - * hashAttributeName = :hashval AND - * rangeAttributeName = :rangeval - *

    - * Valid comparisons for the range key condition are as follows: - *

      - *
    • - *

      - * rangeAttributeName = :rangeval - true if - * the range key is equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName < :rangeval - true if - * the range key is less than :rangeval.

    • - *
    • - *

      - * rangeAttributeName <= :rangeval - true - * if the range key is less than or equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName > :rangeval - true if - * the range key is greater than :rangeval.

    • - *
    • - *

      - * rangeAttributeName >= :rangeval - true - * if the range key is greater than or equal to :rangeval.

    • - *
    • - *

      - * rangeAttributeName BETWEEN :rangeval1 - * AND :rangeval2 - true if the range key is less than - * or greater than :rangeval1, and less than or equal to - * :rangeval2.

    • - *
    • - *

      - * begins_with (rangeAttributeName, - * :rangeval) - true if the range key begins with a - * particular operand. Note that the function name begins_with - * is case-sensitive.

    • - *
    - *

    - * Use ExpressionAttributeValues (via {@link #withExpressionAttributeValues(Map)}) to - * replace tokens such as :hashval and :rangeval - * with actual values at runtime. - *

    - * You can optionally use ExpressionAttributeNames via - * {@link #withExpressionAttributeNames(Map)}) to replace the names of the hash and range - * attributes with placeholder tokens. This might be necessary if an - * attribute name conflicts with a DynamoDB reserved word. For example, the - * following KeyConditionExpression causes an error because - * Size is a reserved word: - *

      - *
    • Size = :myval
    • - *
    - *

    - * To work around this, define a placeholder (such a #myval) to - * represent the attribute name Size. KeyConditionExpression - * then is as follows: - *

      - *
    • #S = - * :myval
    • - *
    - *

    - * For a list of reserved words, see Reserved Words in the Amazon DynamoDB Developer Guide. - *

    - * For more information on ExpressionAttributeNames and - * ExpressionAttributeValues, see Using Placeholders for Attribute Names and Values in the Amazon - * DynamoDB Developer Guide. - *

    - * KeyConditionExpression replaces the legacy KeyConditions - * parameter. - *

    - * When a key expression is specified, the corresponding name-map and - * value-map can optionally be specified via {@link #withExpressionAttributeNames(Map)} and - * {@link #withExpressionAttributeValues(Map)}. - */ - public void setKeyConditionExpression(String keyConditionExpression) { - this.keyConditionExpression = keyConditionExpression; - } - - public DynamoDbQueryExpression withKeyConditionExpression( - String keyConditionExpression) { - this.keyConditionExpression = keyConditionExpression; - return this; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @return One or more substitution variables for simplifying complex - * expressions. - * @see QueryRequest#getExpressionAttributeNames() - */ - public java.util.Map getExpressionAttributeNames() { - - return expressionAttributeNames; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @param expressionAttributeNames - * One or more substitution variables for simplifying complex - * expressions. - * @see QueryRequest#setExpressionAttributeNames(Map) - */ - public void setExpressionAttributeNames( - java.util.Map expressionAttributeNames) { - this.expressionAttributeNames = expressionAttributeNames; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @param expressionAttributeNames - * One or more substitution variables for simplifying complex - * expressions. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see QueryRequest#withExpressionAttributeNames(Map) - */ - public DynamoDbQueryExpression withExpressionAttributeNames( - java.util.Map expressionAttributeNames) { - setExpressionAttributeNames(expressionAttributeNames); - return this; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * The method adds a new key-value pair into ExpressionAttributeNames - * parameter, and returns a reference to this object so that method calls - * can be chained together. - * - * @param key - * The key of the entry to be added into - * ExpressionAttributeNames. - * @param value - * The corresponding value of the entry to be added into - * ExpressionAttributeNames. - * - * @see QueryRequest#addExpressionAttributeNamesEntry(String, String) - */ - public DynamoDbQueryExpression addExpressionAttributeNamesEntry( - String key, String value) { - if (null == this.expressionAttributeNames) { - this.expressionAttributeNames = new java.util.HashMap(); - } - if (this.expressionAttributeNames.containsKey(key)) { - throw new IllegalArgumentException("Duplicated keys (" + key + ") are provided."); - } - this.expressionAttributeNames.put(key, value); - return this; - } - - /** - * Removes all the entries added into ExpressionAttributeNames. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - */ - public DynamoDbQueryExpression clearExpressionAttributeNamesEntries() { - this.expressionAttributeNames = null; - return this; - } - - /** - * One or more values that can be substituted in an expression. - * - * @return One or more values that can be substituted in an expression. - * - * @see QueryRequest#getExpressionAttributeValues() - */ - public java.util.Map getExpressionAttributeValues() { - - return expressionAttributeValues; - } - - /** - * One or more values that can be substituted in an expression. - * - * @param expressionAttributeValues - * One or more values that can be substituted in an expression. - * - * @see QueryRequest#setExpressionAttributeValues(Map) - */ - public void setExpressionAttributeValues( - java.util.Map expressionAttributeValues) { - this.expressionAttributeValues = expressionAttributeValues; - } - - /** - * One or more values that can be substituted in an expression. - * - * @param expressionAttributeValues - * One or more values that can be substituted in an expression. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see QueryRequest#withExpressionAttributeValues(Map) - */ - public DynamoDbQueryExpression withExpressionAttributeValues( - java.util.Map expressionAttributeValues) { - setExpressionAttributeValues(expressionAttributeValues); - return this; - } - - /** - * One or more values that can be substituted in an expression. The method - * adds a new key-value pair into ExpressionAttributeValues parameter, and - * returns a reference to this object so that method calls can be chained - * together. - * - * @param key - * The key of the entry to be added into - * ExpressionAttributeValues. - * @param value - * The corresponding value of the entry to be added into - * ExpressionAttributeValues. - * - * @see QueryRequest#addExpressionAttributeValuesEntry(String, - * AttributeValue) - */ - public DynamoDbQueryExpression addExpressionAttributeValuesEntry( - String key, AttributeValue value) { - if (null == this.expressionAttributeValues) { - this.expressionAttributeValues = new java.util.HashMap(); - } - if (this.expressionAttributeValues.containsKey(key)) { - throw new IllegalArgumentException("Duplicated keys (" + key + ") are provided."); - } - this.expressionAttributeValues.put(key, value); - return this; - } - - /** - * Removes all the entries added into ExpressionAttributeValues. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - */ - public DynamoDbQueryExpression clearExpressionAttributeValuesEntries() { - this.expressionAttributeValues = null; - return this; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @return The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public String select() { - return select; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public void setSelect(String select) { - this.select = select; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public void setSelect(Select select) { - this.select = select.toString(); - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public DynamoDbQueryExpression withSelect(String select) { - this.select = select; - return this; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public DynamoDbQueryExpression withSelect(Select select) { - this.select = select.toString(); - return this; - } - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - * - * @return A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - */ - public String getProjectionExpression() { - return projectionExpression; - } - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - * - * @param projectionExpression A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - */ - public void setProjectionExpression(String projectionExpression) { - this.projectionExpression = projectionExpression; - } - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - *

    - * Returns a reference to this object so that method calls can be chained together. - * - * @param projectionExpression A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - * - * @return A reference to this updated object so that method calls can be chained - * together. - */ - public DynamoDbQueryExpression withProjectionExpression(String projectionExpression) { - this.projectionExpression = projectionExpression; - return this; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the query. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.queryPage}, not {@code DynamoDBMapper.query}. - * - * @return A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public String getReturnConsumedCapacity() { - return returnConsumedCapacity; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the query. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.queryPage}, not {@code DynamoDBMapper.query}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public void setReturnConsumedCapacity(String returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the query. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.queryPage}, not {@code DynamoDBMapper.query}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public void setReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity.toString(); - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the query. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.queryPage}, not {@code DynamoDBMapper.query}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public DynamoDbQueryExpression withReturnConsumedCapacity(String returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity; - return this; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the query. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.queryPage}, not {@code DynamoDBMapper.query}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public DynamoDbQueryExpression withReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity.toString(); - return this; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbRangeKey.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbRangeKey.java deleted file mode 100644 index c2912a8017a8..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbRangeKey.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for marking a property in a class as the range key for a DynamoDB - * table. Applied to the getter method or the class field for the range key - * property. If the annotation is applied directly to the class field, the - * corresponding getter and setter must be declared in the same class. - *

    - * This annotation is required for tables that use a range key. - */ -@DynamoDb -@DynamoDbKeyed(software.amazon.awssdk.services.dynamodb.model.KeyType.RANGE) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbRangeKey { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbSaveExpression.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbSaveExpression.java deleted file mode 100644 index 1b4bd98cb763..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbSaveExpression.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; - -/** - * Enables adding options to a save operation. - * For example, you may want to save only if an attribute has a particular value. - * @see DynamoDbMapper#save(Object, DynamoDbSaveExpression) - */ -public class DynamoDbSaveExpression { - - /** Optional expected attributes. */ - private Map expectedAttributes; - - /** - * The logical operator on the expected value conditions of this save - * operation. - */ - private String conditionalOperator; - - /** - * Gets the map of attribute names to expected attribute values to check on save. - * - * @return The map of attribute names to expected attribute value conditions to check on save - */ - public Map getExpected() { - return expectedAttributes; - } - - /** - * Sets the expected condition to the map of attribute names to expected attribute values given. - * - * @param expectedAttributes - * The map of attribute names to expected attribute value conditions to check on save - */ - public void setExpected(Map expectedAttributes) { - this.expectedAttributes = expectedAttributes; - } - - /** - * Sets the expected condition to the map of attribute names to expected - * attribute values given and returns a pointer to this object for - * method-chaining. - * - * @param expectedAttributes - * The map of attribute names to expected attribute value - * conditions to check on save - */ - public DynamoDbSaveExpression withExpected(Map expectedAttributes) { - setExpected(expectedAttributes); - return this; - } - - /** - * Adds one entry to the expected conditions and returns a pointer to this - * object for method-chaining. - * - * @param attributeName - * The name of the attribute. - * @param expected - * The expected attribute value. - */ - public DynamoDbSaveExpression withExpectedEntry(String attributeName, ExpectedAttributeValue expected) { - if (expectedAttributes == null) { - expectedAttributes = new HashMap(); - } - expectedAttributes.put(attributeName, expected); - return this; - } - - /** - * Returns the logical operator on the expected value conditions of this save - * operation. - */ - public String getConditionalOperator() { - return conditionalOperator; - } - - /** - * Sets the logical operator on the expected value conditions of this save - * operation. - */ - public void setConditionalOperator(String conditionalOperator) { - this.conditionalOperator = conditionalOperator; - } - - /** - * Sets the logical operator on the expected value conditions of this save - * operation. - */ - public void setConditionalOperator(ConditionalOperator conditionalOperator) { - setConditionalOperator(conditionalOperator.toString()); - } - - /** - * Sets the logical operator on the expected value conditions of this save - * operation and returns a pointer to this object for method-chaining. - */ - public DynamoDbSaveExpression withConditionalOperator(String conditionalOperator) { - setConditionalOperator(conditionalOperator); - return this; - } - - /** - * Sets the logical operator on the expected value conditions of this save - * operation and returns a pointer to this object for method-chaining. - */ - public DynamoDbSaveExpression withConditionalOperator(ConditionalOperator conditionalOperator) { - return withConditionalOperator(conditionalOperator.toString()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbScalarAttribute.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbScalarAttribute.java deleted file mode 100644 index 7844543ca3b9..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbScalarAttribute.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -/** - * @Deprecated - Replaced by {@link DynamoDbTyped} - */ -@Deprecated -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbScalarAttribute { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; - - /** - * The scalar attribute type. - * @see software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType - */ - ScalarAttributeType type(); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbScanExpression.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbScanExpression.java deleted file mode 100644 index fe1facd5c22f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbScanExpression.java +++ /dev/null @@ -1,1017 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.ConditionalOperator; -import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.Select; - -/** - * Options for filtering results from a scan operation. For example, callers can - * specify filter conditions so that only objects whose attributes match - * different conditions are returned (see {@link ComparisonOperator} for more - * information on the available comparison types). - * - * @see DynamoDbMapper#scan(Class, DynamoDbScanExpression) - */ -public class DynamoDbScanExpression { - - /** Optional filter to limit the results of the scan. */ - private Map scanFilter; - - /** The exclusive start key for this scan. */ - private Map exclusiveStartKey; - - /** The limit of items to scan during this scan. */ - private Integer limit; - - /** - * The total number of segments into which the scan will be divided. - * Only required for parallel scan operation. - */ - private Integer totalSegments; - - /** - * The ID (zero-based) of the segment to be scanned. - * Only required for parallel scan operation. - */ - private Integer segment; - - /** - * The logical operator on the filter conditions of this scan. - */ - private String conditionalOperator; - - /** - * Evaluates the scan results and returns only the desired values.

    The - * condition you specify is applied to the items scanned; any items that - * do not match the expression are not returned. - */ - private String filterExpression; - - /** - * One or more substitution variables for simplifying complex - * expressions. The following are some use cases for an - * ExpressionAttributeName:

    • Shorten an attribute name that - * is very long or unwieldy in an expression.

    • Create a - * placeholder for repeating occurrences of an attribute name in an - * expression.

    • Prevent special characters in an attribute - * name from being misinterpreted in an expression.

    Use - * the # character in an expression to dereference an attribute - * name. For example, consider the following expression: - *

    • order.customerInfo.LastName = "Smith" OR - * order.customerInfo.LastName = "Jones"

    Now suppose - * that you specified the following for ExpressionAttributeNames: - *

    • {"n":"order.customerInfo.LastName"}

    - *

    The expression can now be simplified as follows: - *

    • #n = "Smith" OR #n = "Jones"

    - */ - private java.util.Map expressionAttributeNames; - - /** - * One or more values that can be substituted in an expression.

    Use - * the : character in an expression to dereference an attribute - * value. For example, consider the following expression: - *

    • ProductStatus IN - * ("Available","Backordered","Discontinued")

    Now - * suppose that you specified the following for - * ExpressionAttributeValues:

    • { - * "a":{"S":"Available"}, "b":{"S":"Backordered"}, - * "d":{"S":"Discontinued"} }

    The expression can now - * be simplified as follows:

    • ProductStatus IN - * (:a,:b,:c)

    - */ - private java.util.Map expressionAttributeValues; - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index.
    • ALL_ATTRIBUTES - Returns all of - * the item attributes from the specified table or index. If you query a - * local secondary index, then for each matching item in the index - * DynamoDB will fetch the entire item from the parent table. If the - * index is configured to project all item attributes, then all of the - * data can be obtained from the local secondary index, and no fetching - * is required.

    • ALL_PROJECTED_ATTRIBUTES - - * Allowed only when querying an index. Retrieves all attributes that - * have been projected into the index. If the index is configured to - * project all attributes, this return value is equivalent to specifying - * ALL_ATTRIBUTES.

    • COUNT - - * Returns the number of matching items, rather than the matching items - * themselves.

    • SPECIFIC_ATTRIBUTES - Returns - * only the attributes listed in AttributesToGet. This return - * value is equivalent to specifying AttributesToGet without - * specifying any value for Select.

      If you query a local - * secondary index and request only attributes that are projected into - * that index, the operation will read only the index and not the table. - * If any of the requested attributes are not projected into the local - * secondary index, DynamoDB will fetch each of these attributes from the - * parent table. This extra fetching incurs additional throughput cost - * and latency.

      If you query a global secondary index, you can only - * request attributes that are projected into the index. Global secondary - * index queries cannot fetch attributes from the parent table.

    • - *

    If neither Select nor AttributesToGet are - * specified, DynamoDB defaults to ALL_ATTRIBUTES when - * accessing a table, and ALL_PROJECTED_ATTRIBUTES when - * accessing an index. You cannot use both Select and - * AttributesToGet together in a single request, unless the value - * for Select is SPECIFIC_ATTRIBUTES. (This usage is - * equivalent to specifying AttributesToGet without any value for - * Select.) - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - */ - private String select; - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - */ - private String projectionExpression; - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the scan. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.scanPage}, not {@code DynamoDBMapper.scan}. - */ - private String returnConsumedCapacity; - - /** - * Optional index name that can be specified for the scan operation. - */ - private String indexName; - - private Boolean consistentRead; - - /** - * Returns the name of the index to be used by this scan; or null if there - * is none. - */ - public String getIndexName() { - return indexName; - } - - /** - * Sets the name of the index to be used by this scan. - */ - public void setIndexName(String indexName) { - this.indexName = indexName; - } - - /** - * Sets the name of the index to be used by this scan. - *

    - * Returns a pointer to this object for method-chaining. - */ - public DynamoDbScanExpression withIndexName(String indexName) { - setIndexName(indexName); - return this; - } - - /** - * Returns the scan filter as a map of attribute names to conditions. - * - * @return The scan filter as a map of attribute names to conditions. - */ - public Map scanFilter() { - return scanFilter; - } - - /** - * Sets the scan filter to the map of attribute names to conditions given. - * - * @param scanFilter - * The map of attribute names to conditions to use when filtering - * scan results. - */ - public void setScanFilter(Map scanFilter) { - this.scanFilter = scanFilter; - } - - /** - * Sets the scan filter to the map of attribute names to conditions given - * and returns a pointer to this object for method-chaining. - * - * @param scanFilter - * The map of attribute names to conditions to use when filtering - * scan results. - */ - public DynamoDbScanExpression withScanFilter(Map scanFilter) { - setScanFilter(scanFilter); - return this; - } - - /** - * Adds a new filter condition to the current scan filter. - * - * @param attributeName - * The name of the attribute on which the specified condition - * operates. - * @param condition - * The condition which describes how the specified attribute is - * compared and if a row of data is included in the results - * returned by the scan operation. - */ - public void addFilterCondition(String attributeName, Condition condition) { - if (scanFilter == null) { - scanFilter = new HashMap(); - } - - scanFilter.put(attributeName, condition); - } - - /** - * Adds a new filter condition to the current scan filter and returns a - * pointer to this object for method-chaining. - * - * @param attributeName - * The name of the attribute on which the specified condition - * operates. - * @param condition - * The condition which describes how the specified attribute is - * compared and if a row of data is included in the results - * returned by the scan operation. - */ - public DynamoDbScanExpression withFilterConditionEntry(String attributeName, Condition condition) { - if (scanFilter == null) { - scanFilter = new HashMap(); - } - - scanFilter.put(attributeName, condition); - return this; - } - - - /** - * Returns the exclusive start key for this scan. - */ - public Map getExclusiveStartKey() { - return exclusiveStartKey; - } - - /** - * Sets the exclusive start key for this scan. - */ - public void setExclusiveStartKey(Map exclusiveStartKey) { - this.exclusiveStartKey = exclusiveStartKey; - } - - /** - * Sets the exclusive start key for this scan and returns a pointer to this - * object for method-chaining. - */ - public DynamoDbScanExpression withExclusiveStartKey(Map exclusiveStartKey) { - this.exclusiveStartKey = exclusiveStartKey; - return this; - } - - /** - * Returns the limit of items to scan during this scan. - *

    - * Use with caution. Please note that this is not the same as the - * number of items to return from the scan operation -- the operation will - * cease and return as soon as this many items are scanned, even if no - * matching results are found. Furthermore, {@link PaginatedScanList} will - * execute as many scan operations as necessary until it either reaches the - * end of the result set as indicated by DynamoDB or enough elements are - * available to fulfill the list operation (e.g. iteration). Therefore, - * except when scanning without a scan filter, it's usually bad practice to - * set a low limit, since doing so will often generate the same amount of - * traffic to DynamoDB but with a greater number of round trips and - * therefore more overall latency. - */ - public Integer limit() { - return limit; - } - - /** - * Sets the limit of items to scan during this scan. Please note that this - * is not the same as the number of items to return from the scan - * operation -- the operation will cease and return as soon as this many - * items are scanned, even if no matching results are found. - * - * @see DynamoDbScanExpression#limit() - */ - public void setLimit(Integer limit) { - this.limit = limit; - } - - /** - * Sets the limit of items to scan and returns a pointer to this object for - * method-chaining. Please note that this is not the same as the - * number of items to return from the scan operation -- the operation will - * cease and return as soon as this many items are scanned, even if no - * matching results are found. - * - * @see DynamoDbScanExpression#limit() - */ - public DynamoDbScanExpression withLimit(Integer limit) { - this.limit = limit; - return this; - } - - /** - * Returns the total number of segments into which the scan will be divided. - */ - public Integer getTotalSegments() { - return totalSegments; - } - - /** - * Sets the total number of segments into which the scan will be divided. - */ - public void setTotalSegments(Integer totalSegments) { - this.totalSegments = totalSegments; - } - - /** - * Sets the total number of segments into which the scan will be divided and - * returns a pointer to this object for method-chaining. - */ - public DynamoDbScanExpression withTotalSegments(Integer totalSegments) { - setTotalSegments(totalSegments); - return this; - } - - /** - * Returns the ID of the segment to be scanned. - */ - public Integer segment() { - return segment; - } - - /** - * Sets the ID of the segment to be scanned. - */ - public void setSegment(Integer segment) { - this.segment = segment; - } - - /** - * Sets the ID of the segment to be scanned and returns a pointer to this - * object for method-chaining. - */ - public DynamoDbScanExpression withSegment(Integer segment) { - setSegment(segment); - return this; - } - - /** - * Returns the logical operator on the filter conditions of this scan. - */ - public String getConditionalOperator() { - return conditionalOperator; - } - - /** - * Sets the logical operator on the filter conditions of this scan. - */ - public void setConditionalOperator(String conditionalOperator) { - this.conditionalOperator = conditionalOperator; - } - - /** - * Sets the logical operator on the filter conditions of this scan. - */ - public void setConditionalOperator(ConditionalOperator conditionalOperator) { - setConditionalOperator(conditionalOperator.toString()); - } - - /** - * Sets the logical operator on the filter conditions of this scan and - * returns a pointer to this object for method-chaining. - */ - public DynamoDbScanExpression withConditionalOperator(String conditionalOperator) { - setConditionalOperator(conditionalOperator); - return this; - } - - /** - * Sets the logical operator on the filter conditions of this scan and - * returns a pointer to this object for method-chaining. - */ - public DynamoDbScanExpression withConditionalOperator(ConditionalOperator conditionalOperator) { - return withConditionalOperator(conditionalOperator.toString()); - } - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - * - * @return Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any - * items that do not match the expression are not returned. - * @see ScanRequest#getFilterExpression() - */ - public String getFilterExpression() { - return filterExpression; - } - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - * - * @param filterExpression - * Evaluates the query results and returns only the desired - * values. - *

    - * The condition you specify is applied to the items queried; any - * items that do not match the expression are not returned. - * @see ScanRequest#setFilterExpression(String) - */ - public void setFilterExpression(String filterExpression) { - this.filterExpression = filterExpression; - } - - /** - * Evaluates the query results and returns only the desired values. - *

    - * The condition you specify is applied to the items queried; any items that - * do not match the expression are not returned. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - * - * @param filterExpression - * Evaluates the query results and returns only the desired - * values. - *

    - * The condition you specify is applied to the items queried; any - * items that do not match the expression are not returned. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see ScanRequest#withFilterExpression(String) - */ - public DynamoDbScanExpression withFilterExpression(String filterExpression) { - this.filterExpression = filterExpression; - return this; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @return One or more substitution variables for simplifying complex - * expressions. - * @see scanRequest#getExpressionAttributeNames() - */ - public java.util.Map getExpressionAttributeNames() { - - return expressionAttributeNames; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @param expressionAttributeNames - * One or more substitution variables for simplifying complex - * expressions. - * @see ScanRequest#setExpressionAttributeNames(Map) - */ - public void setExpressionAttributeNames( - java.util.Map expressionAttributeNames) { - this.expressionAttributeNames = expressionAttributeNames; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * - * @param expressionAttributeNames - * One or more substitution variables for simplifying complex - * expressions. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see ScanRequest#withExpressionAttributeNames(Map) - */ - public DynamoDbScanExpression withExpressionAttributeNames( - java.util.Map expressionAttributeNames) { - setExpressionAttributeNames(expressionAttributeNames); - return this; - } - - /** - * One or more substitution variables for simplifying complex expressions. - * The method adds a new key-value pair into ExpressionAttributeNames - * parameter, and returns a reference to this object so that method calls - * can be chained together. - * - * @param key - * The key of the entry to be added into - * ExpressionAttributeNames. - * @param value - * The corresponding value of the entry to be added into - * ExpressionAttributeNames. - * - * @see ScanRequest#addExpressionAttributeNamesEntry(String, String) - */ - public DynamoDbScanExpression addExpressionAttributeNamesEntry(String key, - String value) { - if (null == this.expressionAttributeNames) { - this.expressionAttributeNames = new java.util.HashMap(); - } - if (this.expressionAttributeNames.containsKey(key)) { - throw new IllegalArgumentException("Duplicated keys (" + key + ") are provided."); - } - this.expressionAttributeNames.put(key, value); - return this; - } - - /** - * Removes all the entries added into ExpressionAttributeNames. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - */ - public DynamoDbScanExpression clearExpressionAttributeNamesEntries() { - this.expressionAttributeNames = null; - return this; - } - - /** - * One or more values that can be substituted in an expression. - * - * @return One or more values that can be substituted in an expression. - * - * @see ScanRequest#getExpressionAttributeValues() - */ - public java.util.Map getExpressionAttributeValues() { - - return expressionAttributeValues; - } - - /** - * One or more values that can be substituted in an expression. - * - * @param expressionAttributeValues - * One or more values that can be substituted in an expression. - * - * @see ScanRequest#setExpressionAttributeValues(Map) - */ - public void setExpressionAttributeValues( - java.util.Map expressionAttributeValues) { - this.expressionAttributeValues = expressionAttributeValues; - } - - /** - * One or more values that can be substituted in an expression. - * - * @param expressionAttributeValues - * One or more values that can be substituted in an expression. - * - * @return A reference to this updated object so that method calls can be - * chained together. - * @see ScanRequest#withExpressionAttributeValues(Map) - */ - public DynamoDbScanExpression withExpressionAttributeValues( - java.util.Map expressionAttributeValues) { - setExpressionAttributeValues(expressionAttributeValues); - return this; - } - - /** - * One or more values that can be substituted in an expression. The method - * adds a new key-value pair into ExpressionAttributeValues parameter, and - * returns a reference to this object so that method calls can be chained - * together. - * - * @param key - * The key of the entry to be added into - * ExpressionAttributeValues. - * @param value - * The corresponding value of the entry to be added into - * ExpressionAttributeValues. - * - * @see ScanRequest#addExpressionAttributeValuesEntry(String, - * AttributeValue) - */ - public DynamoDbScanExpression addExpressionAttributeValuesEntry(String key, - AttributeValue value) { - if (null == this.expressionAttributeValues) { - this.expressionAttributeValues = new java.util.HashMap(); - } - if (this.expressionAttributeValues.containsKey(key)) { - throw new IllegalArgumentException("Duplicated keys (" + key + ") are provided."); - } - this.expressionAttributeValues.put(key, value); - return this; - } - - /** - * Removes all the entries added into ExpressionAttributeValues. - *

    - * Returns a reference to this object so that method calls can be chained - * together. - */ - public DynamoDbScanExpression clearExpressionAttributeValuesEntries() { - this.expressionAttributeValues = null; - return this; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @return The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public String select() { - return select; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public void setSelect(String select) { - this.select = select; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public void setSelect(Select select) { - this.select = select.toString(); - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public DynamoDbScanExpression withSelect(String select) { - this.select = select; - return this; - } - - /** - * The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: ALL_ATTRIBUTES, ALL_PROJECTED_ATTRIBUTES, SPECIFIC_ATTRIBUTES, COUNT - * - * @param select The attributes to be returned in the result. You can retrieve all item - * attributes, specific item attributes, the count of matching items, or - * in the case of an index, some or all of the attributes projected into - * the index. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.Select - */ - public DynamoDbScanExpression withSelect(Select select) { - this.select = select.toString(); - return this; - } - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - * - * @return A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - */ - public String getProjectionExpression() { - return projectionExpression; - } - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - * - * @param projectionExpression A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - */ - public void setProjectionExpression(String projectionExpression) { - this.projectionExpression = projectionExpression; - } - - /** - * A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - *

    - * Returns a reference to this object so that method calls can be chained together. - * - * @param projectionExpression A string that identifies one or more attributes to retrieve from the - * table. These attributes can include scalars, sets, or elements of a - * JSON document. The attributes in the expression must be separated by - * commas.

    If no attribute names are specified, then all attributes - * will be returned. If any of the requested attributes are not found, - * they will not appear in the result.

    For more information, go to Accessing - * Item Attributes in the Amazon DynamoDB Developer Guide. - * - * @return A reference to this updated object so that method calls can be chained - * together. - */ - public DynamoDbScanExpression withProjectionExpression(String projectionExpression) { - this.projectionExpression = projectionExpression; - return this; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the scan. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.scanPage}, not {@code DynamoDBMapper.scan}. - * - * @return A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public String getReturnConsumedCapacity() { - return returnConsumedCapacity; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the scan. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.scanPage}, not {@code DynamoDBMapper.scan}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public void setReturnConsumedCapacity(String returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the scan. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.scanPage}, not {@code DynamoDBMapper.scan}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public void setReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity.toString(); - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the scan. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.scanPage}, not {@code DynamoDBMapper.scan}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public DynamoDbScanExpression withReturnConsumedCapacity(String returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity; - return this; - } - - /** - * A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - *

    - * Returns a reference to this object so that method calls can be chained together. - *

    - * Constraints:
    - * Allowed Values: INDEXES, TOTAL, NONE - *

    - * If enabled, the underlying request to DynamoDB will include the - * configured parameter value and the low-level response from DynamoDB will - * include the amount of capacity consumed by the scan. Currently, the - * consumed capacity is only exposed through the DynamoDBMapper when you - * call {@code DynamoDBMapper.scanPage}, not {@code DynamoDBMapper.scan}. - * - * @param returnConsumedCapacity A value that if set to TOTAL, the response includes - * ConsumedCapacity data for tables and indexes. If set to - * INDEXES, the response includes ConsumedCapacity - * for indexes. If set to NONE (the default), - * ConsumedCapacity is not included in the response. - * - * @return A reference to this updated object so that method calls can be chained - * together. - * - * @see software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity - */ - public DynamoDbScanExpression withReturnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity) { - this.returnConsumedCapacity = returnConsumedCapacity.toString(); - return this; - } - - /** - * Returns whether this scan uses consistent reads. - * - * @see ScanRequest#isConsistentRead() - */ - public Boolean isConsistentRead() { - return consistentRead; - } - - /** - * Sets whether this scan uses consistent reads. - */ - public void setConsistentRead(Boolean consistentRead) { - this.consistentRead = consistentRead; - } - - /** - * Sets whether this scan uses consistent reads and returns a reference - * to this object for method chaining. - */ - public DynamoDbScanExpression withConsistentRead(Boolean consistentRead) { - this.consistentRead = consistentRead; - return this; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTable.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTable.java deleted file mode 100644 index 56dfb28eb356..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTable.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.TableNameOverride; - - -/** - * Annotation to mark a class as a DynamoDB table. - *

    - * This annotation is inherited by subclasses, and can be overridden by them as - * well. - * - * @see TableNameOverride - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Inherited -public @interface DynamoDbTable { - - /** - * The name of the table to use for this class. - */ - String tableName(); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTableMapper.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTableMapper.java deleted file mode 100644 index cac526178e23..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTableMapper.java +++ /dev/null @@ -1,518 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ResourceInUseException; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; - -/** - * A wrapper for {@code DynamoDBMapper} which operates only on a specified - * class/table. All calls are forwarded to the underlying - * {@code DynamoDBMapper} which was used to create this table mapper. - * - * A minimal example using get annotations, - *

    - * @DynamoDBTable(tableName="TestTable")
    - * public class TestClass {
    - *     private Long key;
    - *     private String rangeKey;
    - *     private Double amount;
    - *     private Long version;
    - *
    - *     @DynamoDBHashKey
    - *     public Long getKey() { return key; }
    - *     public void setKey(Long key) { this.key = key; }
    - *
    - *     @DynamoDBRangeKey
    - *     public String getRangeKey() { return rangeKey; }
    - *     public void setRangeKey(String rangeKey) { this.rangeKey = rangeKey; }
    - *
    - *     @DynamoDBAttribute(attributeName="amount")
    - *     public Double getAmount() { return amount; }
    - *     public void setAmount(Double amount) { this.amount = amount; }
    - *
    - *     @DynamoDBVersionAttribute
    - *     public Long getVersion() { return version; }
    - *     public void setVersion(Long version) { this.version = version; }
    - * }
    - * 
    - * - * Initialize the DynamoDB mapper, - *
    - * DynamoDbClient dbClient = new AmazonDynamoDbClient();
    - * DynamoDBMapper dbMapper = new DynamoDBMapper(dbClient);
    - * 
    - * - * Then, create a new table mapper with hash and range key, - *
    - * DynamoDBTableMapper<TestClass,Long,String> mapper = dbMapper.newTableMapper(TestClass.class);
    - * 
    - * - * Or, if the table does not have a range key, - *
    - * DynamoDBTableMapper<TestClass,Long,?> table = dbMapper.newTableMapper(TestClass.class);
    - * 
    - * - * If you don't have your DynamoDB table set up yet, you can use, - *
    - * table.createTableIfNotExists(new ProvisionedThroughput(25L, 25L));
    - * 
    - * - * Save instances of annotated classes and retrieve them, - *
    - * TestClass object = new TestClass();
    - * object.setKey(1234L);
    - * object.setRangeKey("ABCD");
    - * object.setAmount(101D);
    - *
    - * try {
    - *     table.saveIfNotExists(object);
    - * } catch (ConditionalCheckFailedException e) {
    - *     // handle already existing
    - * }
    - * 
    - * - * Execute a query operation, - *
    - * int limit = 10;
    - * List<TestClass> objects = new ArrayList<TestClass>(limit);
    - *
    - * DynamoDBQueryExpression<TestClass> query = new DynamoDBQueryExpression()
    - *     .withRangeKeyCondition(table.rangeKey().name(), table.rangeKey().ge("ABAA"))
    - *     .withQueryFilterEntry("amount", table.field("amount").gt(100D))
    - *     .withHashKeyValues(1234L)
    - *     .withConsistentReads(true);
    - *
    - * QueryResponsePage<TestClass> results = new QueryResponsePage<TestClass>();
    - *
    - * do {
    - *     if (results.lastEvaluatedKey() != null) {
    - *         query.setExclusiveStartKey(results.lastEvaluatedKey());
    - *     }
    - *     query.setLimit(limit - objects.size());
    - *     results = mapper.query(query);
    - *     for (TestClass object : results.getResults()) {
    - *         objects.add(object);
    - *     }
    - * } while (results.lastEvaluatedKey() != null && objects.size() < limit)
    - * 
    - * - * @param The object type which this mapper operates. - * @param The hash key value type. - * @param The range key value type; use ? if no range key. - * - * @see DynamoDbMapper - * @see DynamoDbClient - */ -public final class DynamoDbTableMapper { - - private static final Logger log = LoggerFactory.getLogger(DynamoDbTableMapper.class); - - private final DynamoDbMapperTableModel model; - private final DynamoDbMapperFieldModel hk; - private final DynamoDbMapperFieldModel rk; - private final DynamoDbMapperConfig config; - private final DynamoDbMapper mapper; - private final DynamoDbClient db; - - /** - * Constructs a new table mapper for the given class. - * @param model The field model factory. - * @param mapper The DynamoDB mapper. - * @param db The service object to use for all service calls. - */ - protected DynamoDbTableMapper(DynamoDbClient db, DynamoDbMapper mapper, final DynamoDbMapperConfig config, - final DynamoDbMapperTableModel model) { - this.rk = model.rangeKeyIfExists(); - this.hk = model.hashKey(); - this.model = model; - this.config = config; - this.mapper = mapper; - this.db = db; - } - - /** - * Gets the field model for a given attribute. - * @param The field model's value type. - * @param attributeName The attribute name. - * @return The field model. - */ - public DynamoDbMapperFieldModel field(String attributeName) { - return this.model.field(attributeName); - } - - /** - * Gets the hash key field model for the specified type. - * @param The hash key type. - * @return The hash key field model. - * @throws DynamoDbMappingException If the hash key is not present. - */ - public DynamoDbMapperFieldModel hashKey() { - return this.model.hashKey(); - } - - /** - * Gets the range key field model for the specified type. - * @param The range key type. - * @return The range key field model. - * @throws DynamoDbMappingException If the range key is not present. - */ - public DynamoDbMapperFieldModel rangeKey() { - return this.model.rangeKey(); - } - - /** - * Retrieves multiple items from the table using their primary keys. - * @param itemsToGet The items to get. - * @return The list of objects. - * @see DynamoDbMapper#batchLoad - */ - public List batchLoad(Iterable itemsToGet) { - final Map> results = mapper.batchLoad(itemsToGet); - if (results.isEmpty()) { - return Collections.emptyList(); - } - return (List) results.get(mapper.getTableName(model.targetType(), config)); - } - - /** - * Saves the objects given using one or more calls to the batchWriteItem API. - * @param objectsToSave The objects to save. - * @return The list of failed batches. - * @see DynamoDbMapper#batchSave - */ - public List batchSave(Iterable objectsToSave) { - return mapper.batchWrite(objectsToSave, (Iterable) Collections.emptyList()); - } - - /** - * Deletes the objects given using one or more calls to the batchWtiteItem API. - * @param objectsToDelete The objects to delete. - * @return The list of failed batches. - * @see DynamoDbMapper#batchDelete - */ - public List batchDelete(Iterable objectsToDelete) { - return mapper.batchWrite((Iterable) Collections.emptyList(), objectsToDelete); - } - - /** - * Saves and deletes the objects given using one or more calls to the - * batchWriteItem API. - * @param objectsToWrite The objects to write. - * @param objectsToDelete The objects to delete. - * @return The list of failed batches. - * @see DynamoDbMapper#batchWrite - */ - public List batchWrite(Iterable objectsToWrite, Iterable objectsToDelete) { - return mapper.batchWrite(objectsToWrite, objectsToDelete); - } - - /** - * Loads an object with the hash key given. - * @param hashKey The hash key value. - * @return The object. - * @see DynamoDbMapper#load - */ - public T load(H hashKey) { - return mapper.load(model.targetType(), hashKey); - } - - /** - * Loads an object with the hash and range key. - * @param hashKey The hash key value. - * @param rangeKey The range key value. - * @return The object. - * @see DynamoDbMapper#load - */ - public T load(H hashKey, R rangeKey) { - return mapper.load(model.targetType(), hashKey, rangeKey); - } - - /** - * Saves the object given into DynamoDB. - * @param object The object to save. - * @see DynamoDbMapper#save - */ - public void save(T object) { - mapper.save(object); - } - - /** - * Saves the object given into DynamoDB using the specified saveExpression. - * @param object The object to save. - * @param saveExpression The save expression. - * @see DynamoDbMapper#save - */ - public void save(T object, DynamoDbSaveExpression saveExpression) { - mapper.save(object, saveExpression); - } - - /** - * Saves the object given into DynamoDB with the condition that the hash - * and if applicable, the range key, does not already exist. - * @param object The object to create. - * @throws ConditionalCheckFailedException If the object exists. - * @see DynamoDbMapper#save - * @see DynamoDbSaveExpression - * @see software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue - */ - public void saveIfNotExists(T object) throws ConditionalCheckFailedException { - final DynamoDbSaveExpression saveExpression = new DynamoDbSaveExpression(); - for (final DynamoDbMapperFieldModel key : model.keys()) { - saveExpression.withExpectedEntry(key.name(), ExpectedAttributeValue.builder() - .exists(false).build()); - } - mapper.save(object, saveExpression); - } - - /** - * Saves the object given into DynamoDB with the condition that the hash - * and, if applicable, the range key, already exist. - * @param object The object to update. - * @throws ConditionalCheckFailedException If the object does not exist. - * @see DynamoDbMapper#save - * @see DynamoDbSaveExpression - * @see software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue - */ - public void saveIfExists(T object) throws ConditionalCheckFailedException { - final DynamoDbSaveExpression saveExpression = new DynamoDbSaveExpression(); - for (final DynamoDbMapperFieldModel key : model.keys()) { - saveExpression.withExpectedEntry(key.name(), ExpectedAttributeValue.builder() - .exists(true).value(key.convert(key.get(object))).build()); - } - mapper.save(object, saveExpression); - } - - /** - * Deletes the given object from its DynamoDB table. - * @param object The object to delete. - * @see DynamoDbMapper#delete - */ - public void delete(final T object) { - mapper.delete(object); - } - - /** - * Deletes the given object from its DynamoDB table using the specified - * deleteExpression. - * @param object The object to delete. - * @param deleteExpression The delete expression. - * @see DynamoDbMapper#delete - */ - public void delete(final T object, final DynamoDbDeleteExpression deleteExpression) { - mapper.delete(object, deleteExpression); - } - - /** - * Deletes the given object from its DynamoDB table with the condition that - * the hash and, if applicable, the range key, already exist. - * @param object The object to delete. - * @throws ConditionalCheckFailedException If the object does not exist. - * @see DynamoDbMapper#delete - * @see DynamoDbDeleteExpression - * @see software.amazon.awssdk.services.dynamodb.model.ExpectedAttributeValue - */ - public void deleteIfExists(T object) throws ConditionalCheckFailedException { - final DynamoDbDeleteExpression deleteExpression = new DynamoDbDeleteExpression(); - for (final DynamoDbMapperFieldModel key : model.keys()) { - deleteExpression.withExpectedEntry(key.name(), ExpectedAttributeValue.builder() - .exists(true).value(key.convert(key.get(object))).build()); - } - mapper.delete(object, deleteExpression); - } - - /** - * Evaluates the specified query expression and returns the count of matching - * items, without returning any of the actual item data - * @param queryExpression The query expression. - * @return The count. - * @see DynamoDbMapper#count - */ - public int count(DynamoDbQueryExpression queryExpression) { - return mapper.count(model.targetType(), queryExpression); - } - - /** - * Queries an Amazon DynamoDB table and returns the matching results as an - * unmodifiable list of instantiated objects. - * @param queryExpression The query expression. - * @return The query results. - * @see DynamoDbMapper#query - */ - public PaginatedQueryList query(DynamoDbQueryExpression queryExpression) { - return mapper.query(model.targetType(), queryExpression); - } - - /** - * Queries an Amazon DynamoDB table and returns a single page of matching - * results. - * @param queryExpression The query expression. - * @return The query results. - * @see DynamoDbMapper#query - */ - public QueryResultPage queryPage(DynamoDbQueryExpression queryExpression) { - return mapper.queryPage(model.targetType(), queryExpression); - } - - /** - * Evaluates the specified scan expression and returns the count of matching - * items, without returning any of the actual item data. - * @param scanExpression The scan expression. - * @return The count. - * @see DynamoDbMapper#count - */ - public int count(DynamoDbScanExpression scanExpression) { - return mapper.count(model.targetType(), scanExpression); - } - - /** - * Scans through an Amazon DynamoDB table and returns the matching results - * as an unmodifiable list of instantiated objects. - * @param scanExpression The scan expression. - * @return The scan results. - * @see DynamoDbMapper#scan - */ - public PaginatedScanList scan(DynamoDbScanExpression scanExpression) { - return mapper.scan(model.targetType(), scanExpression); - } - - /** - * Scans through an Amazon DynamoDB table and returns a single page of - * matching results. - * @param scanExpression The scan expression. - * @return The scan results. - * @see DynamoDbMapper#scanPage - */ - public ScanResultPage scanPage(DynamoDbScanExpression scanExpression) { - return mapper.scanPage(model.targetType(), scanExpression); - } - - /** - * Scans through an Amazon DynamoDB table on logically partitioned segments - * in parallel and returns the matching results in one unmodifiable list of - * instantiated objects. - * @param scanExpression The scan expression. - * @param totalSegments The total segments. - * @return The scan results. - * @see DynamoDbMapper#parallelScan - */ - public PaginatedParallelScanList parallelScan(DynamoDbScanExpression scanExpression, int totalSegments) { - return mapper.parallelScan(model.targetType(), scanExpression, totalSegments); - } - - /** - * Returns information about the table, including the current status of the - * table, when it was created, the primary key schema, and any indexes on - * the table. - * @return The describe table results. - * @see DynamoDbClient#describeTable - */ - public TableDescription describeTable() { - return db.describeTable(DescribeTableRequest.builder() - .tableName(mapper.getTableName(model.targetType(), config)) - .build()) - .table(); - } - - /** - * Creates the table with the specified throughput; also populates the same - * throughput for all global secondary indexes. - * @param throughput The provisioned throughput. - * @return The table decription. - * @see DynamoDbClient#createTable - * @see software.amazon.awssdk.services.dynamodb.model.CreateTableRequest - */ - public TableDescription createTable(ProvisionedThroughput throughput) { - CreateTableRequest request = mapper.generateCreateTableRequest(model.targetType()); - CreateTableRequest.Builder modified = request.toBuilder() - .provisionedThroughput(throughput); - if (request.globalSecondaryIndexes() != null) { - modified.globalSecondaryIndexes((Collection) null); - for (GlobalSecondaryIndex gsi : request.globalSecondaryIndexes()) { - gsi = gsi.toBuilder().provisionedThroughput(throughput).build(); - modified.globalSecondaryIndexes(gsi); - } - } - request = modified.build(); - return db.createTable(request).tableDescription(); - } - - /** - * Creates the table and ignores the {@code ResourceInUseException} if it - * ialready exists. - * @param throughput The provisioned throughput. - * @return True if created, or false if the table already existed. - * @see DynamoDbClient#createTable - * @see software.amazon.awssdk.services.dynamodb.model.CreateTableRequest - */ - public boolean createTableIfNotExists(ProvisionedThroughput throughput) { - try { - createTable(throughput); - } catch (final ResourceInUseException e) { - if (log.isTraceEnabled()) { - log.trace("Table already exists, no need to create", e); - } - return false; - } - return true; - } - - /** - * Deletes the table. - * @return The table decription. - * @see DynamoDbClient#deleteTable - * @see software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest - */ - public TableDescription deleteTable() { - return db.deleteTable( - mapper.generateDeleteTableRequest(model.targetType()) - ).tableDescription(); - } - - /** - * Deletes the table and ignores the {@code ResourceNotFoundException} if - * it does not already exist. - * @return True if the table was deleted, or false if the table did not exist. - * @see DynamoDbClient#deleteTable - * @see software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest - */ - public boolean deleteTableIfExists() { - try { - deleteTable(); - } catch (final ResourceNotFoundException e) { - if (log.isTraceEnabled()) { - log.trace("Table does not exist, no need to delete", e); - } - return false; - } - return true; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverted.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverted.java deleted file mode 100644 index 01b5e0758408..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverted.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to mark a property as using a custom type-converter. - * - *

    May be annotated on a user-defined annotation to pass additional - * properties to the {@link DynamoDbTypeConverter}.

    - * - *
    - * @CurrencyFormat(separator=" ") //<- user-defined annotation
    - * public Currency getCurrency()
    - * 
    - * - *

    Where,

    - *
    - * public class Currency {
    - *     private Double amount;
    - *     private String unit;
    - *
    - *     public Double getAmount() { return amount; }
    - *     public void setAmount(Double amount) { this.amount = amount; }
    - *
    - *     public String getUnit() { return unit; }
    - *     public void setUnit(String unit) { this.unit = unit; }
    - * }
    - * 
    - * - *

    And user-defined annotation,

    - *
    - * @Target({ElementType.METHOD})
    - * @Retention(RetentionPolicy.RUNTIME)
    - * @DynamoDBTypeConverted(converter=CurrencyFormat.Converter.class)
    - * public @interface CurrencyFormat {
    - *
    - *     String separator() default " ";
    - *
    - *     public static class Converter implements DynamoDBTypeConverter<String,Currency> {
    - *         private final String separator;
    - *         public Converter(final Class<Currency> targetType, final CurrencyFormat annotation) {
    - *             this.separator = annotation.separator();
    - *         }
    - *         public Converter() {
    - *             this.separator = "|";
    - *         }
    - *         @Override
    - *         public String convert(final Currency o) {
    - *             return String.valueOf(o.getAmount()) + separator + o.getUnit();
    - *         }
    - *         @Override
    - *         public Currency unconvert(final String o) {
    - *             final String[] strings = o.split(separator);
    - *             final Currency currency = new Currency();
    - *             currency.setAmount(Double.valueOf(strings[0]));
    - *             currency.setUnit(strings[1]);
    - *             return currency;
    - *         }
    - *     }
    - * }
    - * 
    - * - *

    Alternately, the property/field may be annotated directly (which - * requires the converter to provide a default constructor or a constructor - * with only the {@code targetType}),

    - *
    - * @DynamoDBTypeConverted(converter=CurrencyFormat.Converter.class)
    - * public Currency getCurrency() { return currency; }
    - * 
    - * - *

    All converters are null-safe, a {@code null} value will never be passed - * to {@link DynamoDbTypeConverter#convert} - * or {@link DynamoDbTypeConverter#unconvert}.

    - * - *

    Precedence for selecting a type-converter first goes to getter annotations, - * then field, then finally type.

    - * - *

    May be used in combination with {@link DynamoDbTyped} to specify the - * attribute type binding.

    - *

    Compatible with {@link DynamoDbAutoGeneratedTimestamp}

    - * - *

    May be used as a meta-annotation.

    - */ -@DynamoDb -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbTypeConverted { - - /** - * The class of the converter for this property. - */ - @SuppressWarnings("rawtypes") - Class converter(); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedEnum.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedEnum.java deleted file mode 100644 index a05e63d1bb4d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedEnum.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation to convert the enumeration value to a string. - * - *

    Alternately, the {@link DynamoDbTyped} annotation may be used,

    - *
    - * public static enum Status { OPEN, PENDING, CLOSED }
    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.S)
    - * public Status status()
    - * 
    - * - *

    Please note, there are some risks in distributed systems when using - * enumerations as attributes intead of simply using a String. - * When adding new values to the enumeration, the enum only changes must - * be deployed before the enumeration value can be persisted. This will - * ensure that all systems have the correct code to map it from the item - * record in DynamoDB to your objects.

    - * - * @see DynamoDbTypeConverted - */ -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.S) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbTypeConvertedEnum { - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedJson.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedJson.java deleted file mode 100644 index 721ccd7badcd..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedJson.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * A simple JSON converter that uses the Jackson JSON processor. - * - *

    It shares all limitations of that library. For more information about - * Jackson, see: http://wiki.fasterxml.com/JacksonHome

    - * - *
    - * @DynamoDBTypeConvertedJson
    - * public Currency getCurrency()
    - * 
    - * - *

    Where,

    - *
    - * public class Currency {
    - *     private Double amount;
    - *     private String unit;
    - *
    - *     public Double getAmount() { return amount; }
    - *     public void setAmount(Double amount) { this.amount = amount; }
    - *
    - *     public String getUnit() { return unit; }
    - *     public void setUnit(String unit) { this.unit = unit; }
    - * }
    - * 
    - * - *

    Would write the following value to DynamoDB given,

    - *
      - *
    • Currency(79.99,"USD") = "{\"amount\":79.99,\"unit\":\"USD\"}"
    • - *
    - * - * @see DynamoDbTypeConverted - */ -@DynamoDbTypeConverted(converter = DynamoDbTypeConvertedJson.Converter.class) -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.S) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbTypeConvertedJson { - - /** - * The value type to use when calling the JSON mapper's {@code readValue}; - * a value of {@code Void.class} indicates to use the getter's type. - */ - Class targetType() default void.class; - - /** - * JSON type converter. - */ - final class Converter implements DynamoDbTypeConverter { - private static final ObjectMapper MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - private final Class targetType; - - Converter(Class targetType, DynamoDbTypeConvertedJson annotation) { - this.targetType = annotation.targetType() == void.class ? targetType : (Class) annotation.targetType(); - } - - @Override - public String convert(final T object) { - try { - return MAPPER.writeValueAsString(object); - } catch (final Exception e) { - throw new DynamoDbMappingException("Unable to write object to JSON", e); - } - } - - @Override - public T unconvert(final String object) { - try { - return MAPPER.readValue(object, targetType); - } catch (final Exception e) { - throw new DynamoDbMappingException("Unable to read JSON string", e); - } - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedTimestamp.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedTimestamp.java deleted file mode 100644 index b6a6429a94d6..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConvertedTimestamp.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Scalar.TIME_ZONE; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.util.Date; -import java.util.TimeZone; - -/** - * Annotation to format a timestamp object using Java's standard date and time - * patterns. - * - *
    - * @DynamoDBTypeConvertedTimestamp(pattern="yyyyMMddHHmmssSSS", timeZone="UTC")
    - * public Date getCreatedDate()
    - * 
    - * - *

    Supports the standard {@link Date} type-conversions; such as - * {@link java.util.Calendar}, {@link Long}.

    - * - *

    Primitives such as {@code long} are not supported since the unset - * (or null) state can't be detected.

    - * - *

    Compatible with {@link DynamoDbAutoGeneratedTimestamp}

    - * - * @see DynamoDbAutoGeneratedTimestamp - * @see DynamoDbTypeConverted - * @see java.text.SimpleDateFormat - * @see java.util.TimeZone - */ -@DynamoDbTypeConverted(converter = DynamoDbTypeConvertedTimestamp.Converter.class) -@DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.S) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbTypeConvertedTimestamp { - - /** - * The pattern format; default is ISO8601. - * @see java.text.SimpleDateFormat - */ - String pattern() default "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - - /** - * The time zone; default is {@code UTC}. - * @see java.util.TimeZone - */ - String timeZone() default "UTC"; - - /** - * Timestamp format converter. - */ - final class Converter implements DynamoDbTypeConverter { - private final DynamoDbTypeConverter converter; - private final DateTimeFormatter formatter; - - Converter(Class targetType, DynamoDbTypeConvertedTimestamp annotation) { - this.formatter = new DateTimeFormatterBuilder() - .appendPattern(annotation.pattern()).toFormatter() - .withZone(TIME_ZONE.convert(annotation.timeZone()).toZoneId()); - this.converter = StandardTypeConverters.factory().getConverter(ZonedDateTime.class, targetType); - } - - @Override - public String convert(final T object) { - return formatter.format(converter.convert(object)); - } - - @Override - public T unconvert(final String object) { - return converter.unconvert(ZonedDateTime.parse(object, formatter)); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverter.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverter.java deleted file mode 100644 index 747f5e2d51f9..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverter.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import software.amazon.awssdk.annotations.SdkInternalApi; - -/** - * Interface for converting types. - * - * @param The DynamoDB standard type. - * @param The object's field/property type. - */ -public interface DynamoDbTypeConverter { - - /** - * Turns an object of type T into an object of type S. - */ - S convert(T object); - - /** - * Turns an object of type S into an object of type T. - */ - T unconvert(S object); - - /** - * An abstract converter with additional general purpose functions. - */ - @SdkInternalApi - abstract class AbstractConverter implements DynamoDbTypeConverter { - public static ExtendedConverter join(DynamoDbTypeConverter source, - DynamoDbTypeConverter target) { - return new ExtendedConverter(source, target); - } - - public static NullSafeConverter nullSafe(DynamoDbTypeConverter converter) { - return new NullSafeConverter(converter); - } - - public DynamoDbTypeConverter joinAll(DynamoDbTypeConverter... targets) { - AbstractConverter converter = (AbstractConverter) nullSafe(); - for (DynamoDbTypeConverter target : targets) { - if (target != null) { - converter = converter.join((DynamoDbTypeConverter) nullSafe(target)); - } - } - return converter; - } - - public ExtendedConverter join(DynamoDbTypeConverter target) { - return AbstractConverter.join(this, target); - } - - public NullSafeConverter nullSafe() { - return AbstractConverter.nullSafe(this); - } - } - - /** - * A converter which wraps a source and target converter. - */ - class ExtendedConverter extends AbstractConverter { - private final DynamoDbTypeConverter source; - private final DynamoDbTypeConverter target; - - public ExtendedConverter(DynamoDbTypeConverter source, DynamoDbTypeConverter target) { - this.source = source; - this.target = target; - } - - @Override - public S convert(final T o) { - U o1 = target.convert(o); - S o2 = source.convert(o1); - return o2; - //return source.convert(target.convert(o)); - } - - @Override - public T unconvert(final S o) { - return target.unconvert(source.unconvert(o)); - } - } - - /** - * A general purpose delegating converter. - */ - class DelegateConverter extends AbstractConverter { - private final DynamoDbTypeConverter delegate; - - public DelegateConverter(DynamoDbTypeConverter delegate) { - this.delegate = delegate; - } - - @Override - public S convert(final T object) { - return delegate.convert(object); - } - - @Override - public T unconvert(final S object) { - return delegate.unconvert(object); - } - } - - /** - * A converter which evaluates nullability before convert/unconvert. - */ - class NullSafeConverter extends DelegateConverter { - public NullSafeConverter(DynamoDbTypeConverter delegate) { - super(delegate); - } - - @Override - public S convert(final T object) { - return object == null ? null : super.convert(object); - } - - @Override - public T unconvert(final S object) { - return object == null ? null : super.unconvert(object); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverterFactory.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverterFactory.java deleted file mode 100644 index 604b94406e28..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTypeConverterFactory.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.AbstractMap.SimpleImmutableEntry; -import java.util.LinkedHashMap; -import java.util.Map.Entry; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Vector; - -/** - * {@link DynamoDbTypeConverter} factory and supporting classes. - * - *

    To override standard type-conversions,

    - *
    - * DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    - *     .withTypeConverterFactory(DynamoDBTypeConverterFactory.standard().override()
    - *         .with(String.class, MyObject.class, new StringToMyObjectConverter())
    - *         .build())
    - *     .build();
    - * 
    - *

    Then, on the property, specify the attribute binding,

    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.S)
    - * public MyObject myObject()
    - * 
    - * - * @see DynamoDbMapperConfig - */ -public abstract class DynamoDbTypeConverterFactory { - - /** - * Returns the standard type-converter factory. To override, the factory, - * @see DynamoDbTypeConverterFactory#override - */ - public static DynamoDbTypeConverterFactory standard() { - return StandardTypeConverters.factory(); - } - - /** - * Gets the type-converter matching the target conversion type. - * @param The DynamoDB standard type. - * @param The object's field/property type. - * @param sourceType The source conversion type. - * @param targetType The target conversion type. - * @return The type-converter, or null if no match. - */ - public abstract DynamoDbTypeConverter getConverter(Class sourceType, Class targetType); - - /** - * Creates a type-converter factory builder using this factory as defaults. - */ - public final Builder override() { - return new Builder(this); - } - - /** - * Builder for overriding type-converters. - */ - public static final class Builder { - private final ConverterMap overrides = new ConverterMap(); - private final DynamoDbTypeConverterFactory defaults; - - private Builder(DynamoDbTypeConverterFactory defaults) { - this.defaults = defaults; - } - - public Builder with(Class sourceType, Class targetType, - DynamoDbTypeConverter converter) { - if (Vector.SET.is(sourceType) || Vector.LIST.is(sourceType) || Vector.MAP.is(sourceType)) { - throw new DynamoDbMappingException("type [" + sourceType + "] is not supported" + - "; type-converter factory only supports scalar conversions"); - } - overrides.put(sourceType, targetType, converter); - return this; - } - - public DynamoDbTypeConverterFactory build() { - return new OverrideFactory(defaults, overrides); - } - } - - /** - * A delegating {@link DynamoDbTypeConverterFactory}. - */ - public static class DelegateFactory extends DynamoDbTypeConverterFactory { - private final DynamoDbTypeConverterFactory delegate; - - public DelegateFactory(DynamoDbTypeConverterFactory delegate) { - this.delegate = delegate; - } - - @Override - public DynamoDbTypeConverter getConverter(Class sourceType, Class targetType) { - return delegate.getConverter(sourceType, targetType); - } - } - - /** - * Delegate factory to allow selected types to be overridden. - */ - private static class OverrideFactory extends DelegateFactory { - private final ConverterMap overrides; - - OverrideFactory(DynamoDbTypeConverterFactory defaults, ConverterMap overrides) { - super(defaults); - this.overrides = overrides; - } - - @Override - public DynamoDbTypeConverter getConverter(Class sourceType, Class targetType) { - DynamoDbTypeConverter converter = overrides.get(sourceType, targetType); - if (converter == null) { - converter = super.getConverter(sourceType, targetType); - } - return converter; - } - } - - /** - * Map of source and target pairs to the converter. - */ - private static final class ConverterMap extends LinkedHashMap, DynamoDbTypeConverter> { - private static final long serialVersionUID = -1L; - - public void put(Class sourceType, Class targetType, - DynamoDbTypeConverter converter) { - put(Key.of(sourceType, targetType), converter); - } - - @SuppressWarnings("unchecked") - public DynamoDbTypeConverter get(Class sourceType, Class targetType) { - for (final Entry, DynamoDbTypeConverter> entry : entrySet()) { - if (entry.getKey().isAssignableFrom(sourceType, targetType)) { - return (DynamoDbTypeConverter) entry.getValue(); - } - } - return null; - } - } - - /** - * Source and target conversion type pair. - */ - private static final class Key extends SimpleImmutableEntry, Class> { - private static final long serialVersionUID = -1L; - - private Key(Class sourceType, Class targetType) { - super(sourceType, targetType); - } - - public static Key of(Class sourceType, Class targetType) { - return new Key(sourceType, targetType); - } - - public boolean isAssignableFrom(Class sourceType, Class targetType) { - return getKey().isAssignableFrom(sourceType) && getValue().isAssignableFrom(targetType); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTyped.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTyped.java deleted file mode 100644 index 0deb4cedc976..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbTyped.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; - -/** - * Annotation to override the standard attribute type binding. - * - *
    - * @DynamoDBTyped(DynamoDBAttributeType.S)
    - * public MyObject myObject()
    - * 
    - - *

    Standard Types

    - *

    Standard types do not require the annotation if applying the default - * attribute binding for that type.

    - *

    String/{@code S} types,

    - *
      - *
    • {@link java.lang.Character}/{@code char}
    • - *
    • {@link java.lang.String}
    • - *
    • {@link java.net.URL}
    • - *
    • {@link java.net.URI}
    • - *
    • {@link java.util.Calendar}
    • - *
    • {@link java.util.Currency}
    • - *
    • {@link java.util.Date}
    • - *
    • {@link java.util.Locale}
    • - *
    • {@link java.util.TimeZone}
    • - *
    • {@link java.util.UUID}
    • - *
    • {@link S3Link}
    • - *
    - *

    Number/{@code N} types,

    - *
      - *
    • {@link java.math.BigDecimal}
    • - *
    • {@link java.math.BigInteger}
    • - *
    • {@link java.lang.Boolean}/{@code boolean}
    • - *
    • {@link java.lang.Byte}/{@code byte}
    • - *
    • {@link java.lang.Double}/{@code double}
    • - *
    • {@link java.lang.Float}/{@code float}
    • - *
    • {@link java.lang.Integer}/{@code int}
    • - *
    • {@link java.lang.Long}/{@code long}
    • - *
    • {@link java.lang.Short}/{@code short}
    • - *
    - *

    Binary/{@code B} types,

    - *
      - *
    • {@link java.nio.ByteBuffer}
    • - *
    • {@code byte[]}
    • - *
    - * - *

    {@link DynamoDbTypeConverter}

    - *

    A custom type-converter maybe applied to any attribute, either by - * annotation or by overriding the standard type-converter factory.

    - *
    - * DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    - *     .withTypeConverterFactory(DynamoDBTypeConverterFactory.standard().override()
    - *         .with(String.class, MyObject.class, new StringToMyObjectConverter())
    - *         .build())
    - *     .build();
    - * 
    - *

    If the converter being applied is already a supported data type and - * the conversion is of the same attribute type, for instance, - * {@link java.util.Date} to {@link String} to {@code S}, - * the annotation may be omitted. The annotation is require for all non-standard - * types or if the attribute type binding is being overridden.

    - * - *

    {@link software.amazon.awssdk.services.dynamodb.model.AttributeValue}

    - *

    Direct native conversion is supported by default in all schemas. - * If the attribute is a primary or index key, it must specify either - * {@code B}, {@code N}, or {@code S}, otherwise, it may be omitted.

    - * - *

    {@link Boolean} to {@code BOOL}

    - *

    The standard V2 conversion schema will by default serialize booleans - * natively using the DynamoDB {@code BOOL} type.

    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.BOOL)
    - * public boolean isTesting()
    - * 
    - * - *

    {@link Boolean} to {@code N}

    - *

    The standard V1 and V2 compatible conversion schemas will by default - * serialize booleans using the DynamoDB {@code N} type, with a value of '1' - * representing 'true' and a value of '0' representing 'false'.

    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.N)
    - * public boolean isTesting()
    - * 
    - * - *

    {@link Enum} to {@code S}

    - *

    The {@code enum} type is only supported by override or custom converter. - * There are some risks in distributed systems when using enumerations as - * attributes intead of simply using a String. When adding new values to the - * enumeration, the enum only changes must deployed before the enumeration - * value can be persisted. This will ensure that all systems have the correct - * code to map it from the item record in DynamoDB to your objects.

    - *
    - * public enum Status { OPEN, PENDING, CLOSED };
    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.S)
    - * public Status status()
    - * 
    - * - *

    {@link UUID} to {@code B}

    - *

    The {@code UUID} type will serialize to {@link String}/{@code S} by - * default in all conversion schemas. The schemas do support serializing to - * {@link ByteBuffer}/{@code B} by override.

    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.B)
    - * public UUID getKey()
    - * 
    - * - *

    {@link Set} to {@code L}

    - *

    The standard V1 and V2 compatible conversion schemas do not by default - * support non-scalar {@code Set} types. They are supported in V2. In - * non-supported schemas, the {@link List}/{@code L} override may be applied - * to any {@code Set} type.

    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.L)
    - * public Set<MyObject> myObjects()
    - * 
    - * - *

    {@link Object} to {@code M}

    - *

    Also supported as {@link DynamoDbDocument}.

    - *
    - * @DynamoDBTyped(DynamoDBAttributeType.M)
    - * public MyObject myObject()
    - * 
    - * - *

    May be combined with {@link DynamoDbTypeConverted}.

    - * - *

    May be used as a meta-annotation.

    - * - * @see DynamoDbTypeConverted - * @see DynamoDbTypeConverterFactory - */ -@DynamoDb -@Inherited -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbTyped { - - /** - * Use when the type of the attribute as stored in DynamoDB should differ - * from the standard type assigned by DynamoDBMapper. - */ - DynamoDbAttributeType value() default DynamoDbAttributeType.NULL; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbVersionAttribute.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbVersionAttribute.java deleted file mode 100644 index bc1edd0ae667..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbVersionAttribute.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; - -/** - * Annotation for marking a property as an optimistic locking version attribute. - * - *

    Applied to the getter method or the class field for the class's version - * property. If the annotation is applied directly to the class field, the - * corresponding getter and setter must be declared in the same class. - * - *

    Alternately, the meta-annotation {@link DynamoDbVersioned} may be used - * to annotate a custom annotation, or directly to the field/getter.

    - * - *

    Only nullable, integral numeric types (e.g. Integer, Long) can be used as - * version properties. On a save() operation, the {@link DynamoDbMapper} will - * attempt to increment the version property and assert that the service's value - * matches the client's. New objects will be assigned a version of 1 when saved. - *

    - * Note that for batchWrite, and by extension batchSave and batchDelete, no - * version checks are performed, as required by the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} - * API. - * - * @see DynamoDbVersioned - */ -@DynamoDb -@DynamoDbVersioned -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD}) -public @interface DynamoDbVersionAttribute { - - /** - * Optional parameter when the name of the attribute as stored in DynamoDB - * should differ from the name used by the getter / setter. - */ - String attributeName() default ""; - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbVersioned.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbVersioned.java deleted file mode 100644 index 5a7fd63b1503..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/DynamoDbVersioned.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.math.BigInteger; -import java.util.Arrays; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Scalar; - -/** - * Annotation for marking a property as an optimistic locking version attribute. - * - *

    - * @DynamoDBVersioned
    - * public Long getRecordVersionNumber()
    - * 
    - * - *

    Alternately, the convinience annotation {@link DynamoDbVersionAttribute} - * may be used if combining with an attribute name on a field/getter.

    - * - *

    Only nullable, integral numeric types (e.g. Integer, Long) can be used as - * version properties. On a save() operation, the {@link DynamoDbMapper} will - * attempt to increment the version property and assert that the service's value - * matches the client's.

    - * - *

    New objects will be assigned a version of 1 when saved.

    - * - *

    Note that for batchWrite, and by extension batchSave and batchDelete, - * no version checks are performed, as required by the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} - * API.

    - * - *

    May be used as a meta-annotation.

    - * - * @see DynamoDbVersionAttribute - */ -@DynamoDb -@DynamoDbAutoGenerated(generator = DynamoDbVersioned.Generator.class) -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) -public @interface DynamoDbVersioned { - - /** - * Version auto-generator. - */ - final class Generator extends DynamoDbAutoGenerator.AbstractGenerator { - private final Sequence sequence; - - Generator(Class targetType, DynamoDbVersioned annotation) { - super(DynamoDbAutoGenerateStrategy.ALWAYS); - this.sequence = Sequences.of(targetType); - } - - @Override - public T generate(final T currentValue) { - return currentValue == null ? sequence.init() : sequence.next(currentValue); - } - - private enum Sequences { - BIG_INTEGER(Scalar.BIG_INTEGER, new Sequence() { - @Override - public BigInteger init() { - return BigInteger.ONE; - } - - @Override - public BigInteger next(final BigInteger o) { - return o.add(BigInteger.ONE); - } - }), - - BYTE(Scalar.BYTE, new Sequence() { - @Override - public Byte init() { - return Byte.valueOf((byte) 1); - } - - @Override - public Byte next(final Byte o) { - return (byte) ((o + 1) % Byte.MAX_VALUE); - } - }), - - INTEGER(Scalar.INTEGER, new Sequence() { - @Override - public Integer init() { - return Integer.valueOf(1); - } - - @Override - public Integer next(final Integer o) { - return o + 1; - } - }), - - LONG(Scalar.LONG, new Sequence() { - @Override - public Long init() { - return Long.valueOf(1L); - } - - @Override - public Long next(final Long o) { - return o + 1L; - } - }), - - SHORT(Scalar.SHORT, new Sequence() { - @Override - public Short init() { - return Short.valueOf((short) 1); - } - - @Override - public Short next(final Short o) { - return (short) (o + 1); - } - }); - - private final Sequence sequence; - private final Scalar scalar; - - Sequences(final Scalar scalar, final Sequence sequence) { - this.sequence = sequence; - this.scalar = scalar; - } - - private static Sequence of(final Class targetType) { - for (final Sequences s : Sequences.values()) { - if (s.scalar.is(targetType)) { - return (Sequence) s.sequence; - } - } - throw new DynamoDbMappingException( - "type [" + targetType + "] is not supported; allowed only " + Arrays.toString(Sequences.values()) - ); - } - } - - interface Sequence { - T init(); - - T next(T o); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/GenerateDeleteTableRequestTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/GenerateDeleteTableRequestTest.java deleted file mode 100644 index 03688421a463..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/GenerateDeleteTableRequestTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; - -/** - * Unit tests for {@link DynamoDbMapper#generateDeleteTableRequest(Class)}. - */ -public class GenerateDeleteTableRequestTest { - - private static final String TABLE_PREFIX = "DEV-"; - private static final String TABLE_NAME = "OBJECTORMEXAMPLE"; - - @Test - public void tableNameNotOverriden_UsesTableNameAttributeInAnnotation() { - DynamoDbMapper dynamoDBMapper = new DynamoDbMapper(null); - DeleteTableRequest deleteTableRequest = dynamoDBMapper.generateDeleteTableRequest(ObjectORMExample.class); - assertEquals(deleteTableRequest.tableName(), TABLE_NAME); - } - - @Test - public void tableNameOverriddenInConfig_UsesPrefixedOverrideTableName() { - DynamoDbMapperConfig.TableNameOverride tableNameOverride = DynamoDbMapperConfig.TableNameOverride - .withTableNamePrefix(TABLE_PREFIX); - DynamoDbMapperConfig config = new DynamoDbMapperConfig(tableNameOverride); - DynamoDbMapper dynamoDBMapper = new DynamoDbMapper(null, config); - - DeleteTableRequest deleteTableRequest = dynamoDBMapper.generateDeleteTableRequest(ObjectORMExample.class); - assertEquals(deleteTableRequest.tableName(), TABLE_PREFIX.concat(TABLE_NAME)); - } - - @DynamoDbTable(tableName = TABLE_NAME) - private static class ObjectORMExample { - private String id; - - @DynamoDbHashKey - public final String getId() { - return this.id; - } - - public final void setId(String id) { - this.id = id; - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/IDynamoDbMapper.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/IDynamoDbMapper.java deleted file mode 100644 index 090615becfdd..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/IDynamoDbMapper.java +++ /dev/null @@ -1,690 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper.FailedBatch; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.PaginationLoadingStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.SaveBehavior; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.BatchGetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; - -/** - * Interface for DynamoDBMapper. - * - *

    - * Note: Do not implement this interface, extend from {@link AbstractDynamoDbMapper} instead. - *

    - * - * @see DynamoDbMapper - * @see AbstractDynamoDbMapper - */ -public interface IDynamoDbMapper { - /** - * Get the table model for the class, using the default configuration. - * - * @see DynamoDbMapper#getTableModel(Class, DynamoDbMapperConfig) - */ - DynamoDbMapperTableModel getTableModel(Class clazz); - - /** - * Get the table model for the class using the provided configuration override. - */ - DynamoDbMapperTableModel getTableModel(Class clazz, DynamoDbMapperConfig config); - - /** - * Loads an object with the hash key given and a configuration override. This configuration - * overrides the default provided at object construction. - * - * @see DynamoDbMapper#load(Class, Object, Object, DynamoDbMapperConfig) - */ - T load(Class clazz, Object hashKey, DynamoDbMapperConfig config); - - /** - * Loads an object with the hash key given, using the default configuration. - * - * @see DynamoDbMapper#load(Class, Object, Object, DynamoDbMapperConfig) - */ - T load(Class clazz, Object hashKey); - - /** - * Loads an object with a hash and range key, using the default configuration. - * - * @see DynamoDbMapper#load(Class, Object, Object, DynamoDbMapperConfig) - */ - T load(Class clazz, Object hashKey, Object rangeKey); - - /** - * Returns an object whose keys match those of the prototype key object given, or null if no - * such item exists. - * - * @param keyObject - * An object of the class to load with the keys values to match. - * @see DynamoDbMapper#load(Object, DynamoDbMapperConfig) - */ - T load(T keyObject); - - /** - * Returns an object whose keys match those of the prototype key object given, or null if no - * such item exists. - * - * @param keyObject - * An object of the class to load with the keys values to match. - * @param config - * Configuration for the service call to retrieve the object from DynamoDB. This - * configuration overrides the default given at construction. - */ - T load(T keyObject, DynamoDbMapperConfig config); - - /** - * Returns an object with the given hash key, or null if no such object exists. - * - * @param clazz - * The class to load, corresponding to a DynamoDB table. - * @param hashKey - * The key of the object. - * @param rangeKey - * The range key of the object, or null for tables without a range key. - * @param config - * Configuration for the service call to retrieve the object from DynamoDB. This - * configuration overrides the default given at construction. - */ - T load(Class clazz, Object hashKey, Object rangeKey, DynamoDbMapperConfig config); - - /** - * Creates and fills in the attributes on an instance of the class given with the attributes - * given. - *

    - * This is accomplished by looking for getter methods annotated with an appropriate annotation, - * then looking for matching attribute names in the item attribute map. - *

    - * This method is no longer called by load/scan/query methods. If you are overriding this - * method, please switch to using an AttributeTransformer - * - * @param clazz - * The class to instantiate and hydrate - * @param itemAttributes - * The set of item attributes, keyed by attribute name. - */ - T marshallIntoObject(Class clazz, Map itemAttributes); - - /** - * Unmarshalls the list of item attributes into objects of type clazz. - *

    - * This method is no longer called by load/scan/query methods. If you are overriding this - * method, please switch to using an AttributeTransformer - * - * @see DynamoDbMapper#marshallIntoObject(Class, Map) - */ - List marshallIntoObjects(Class clazz, List> itemAttributes); - - /** - * Saves the object given into DynamoDB, using the default configuration. - * - * @see DynamoDbMapper#save(Object, DynamoDbSaveExpression, DynamoDbMapperConfig) - */ - void save(T object); - - /** - * Saves the object given into DynamoDB, using the default configuration and the specified - * saveExpression. - * - * @see DynamoDbMapper#save(Object, DynamoDbSaveExpression, DynamoDbMapperConfig) - */ - void save(T object, DynamoDbSaveExpression saveExpression); - - /** - * Saves the object given into DynamoDB, using the specified configuration. - * - * @see DynamoDbMapper#save(Object, DynamoDbSaveExpression, DynamoDbMapperConfig) - */ - void save(T object, DynamoDbMapperConfig config); - - /** - * Saves an item in DynamoDB. The service method used is determined by the - * {@link DynamoDbMapperConfig#saveBehavior()} value, to use either - * {@link DynamoDbClient#putItem(PutItemRequest)} or - * {@link DynamoDbClient#updateItem(UpdateItemRequest)}: - *

      - *
    • UPDATE (default) : UPDATE will not affect unmodeled attributes on a save operation - * and a null value for the modeled attribute will remove it from that item in DynamoDB. Because - * of the limitation of updateItem request, the implementation of UPDATE will send a putItem - * request when a key-only object is being saved, and it will send another updateItem request if - * the given key(s) already exists in the table.
    • - *
    • UPDATE_SKIP_NULL_ATTRIBUTES : Similar to UPDATE except that it ignores any null - * value attribute(s) and will NOT remove them from that item in DynamoDB. It also guarantees to - * send only one single updateItem request, no matter the object is key-only or not.
    • - *
    • CLOBBER : CLOBBER will clear and replace all attributes, included unmodeled ones, - * (delete and recreate) on save. Versioned field constraints will also be disregarded.
    • - *
    - * Any options specified in the saveExpression parameter will be overlaid on any constraints due - * to versioned attributes. - * - * @param object - * The object to save into DynamoDB - * @param saveExpression - * The options to apply to this save request - * @param config - * The configuration to use, which overrides the default provided at object - * construction. - * @see DynamoDbMapperConfig.SaveBehavior - */ - void save(T object, DynamoDbSaveExpression saveExpression, DynamoDbMapperConfig config); - - /** - * Deletes the given object from its DynamoDB table using the default configuration. - */ - void delete(Object object); - - /** - * Deletes the given object from its DynamoDB table using the specified deleteExpression and - * default configuration. - */ - void delete(Object object, DynamoDbDeleteExpression deleteExpression); - - /** - * Deletes the given object from its DynamoDB table using the specified configuration. - */ - void delete(Object object, DynamoDbMapperConfig config); - - /** - * Deletes the given object from its DynamoDB table using the provided deleteExpression and - * provided configuration. Any options specified in the deleteExpression parameter will be - * overlaid on any constraints due to versioned attributes. - * - * @param deleteExpression - * The options to apply to this delete request - * @param config - * Config override object. If {@link SaveBehavior#CLOBBER} is supplied, version - * fields will not be considered when deleting the object. - */ - void delete(T object, DynamoDbDeleteExpression deleteExpression, DynamoDbMapperConfig config); - - /** - * Deletes the objects given using one or more calls to the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. No version checks are - * performed, as required by the API. - * - * @see DynamoDbMapper#batchWrite(Iterable, Iterable) - */ - List batchDelete(Iterable objectsToDelete); - - /** - * Deletes the objects given using one or more calls to the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. No version checks are - * performed, as required by the API. - * - * @see DynamoDbMapper#batchWrite(Iterable, Iterable) - */ - List batchDelete(Object... objectsToDelete); - - /** - * Saves the objects given using one or more calls to the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. No version checks are - * performed, as required by the API. - *

    - * This method ignores any SaveBehavior set on the mapper, and always behaves as if - * SaveBehavior.CLOBBER was specified, as the DynamoDbClient.batchWriteItem() request does not - * support updating existing items. - *

    - * This method fails to save the batch if the size of an individual object in the batch exceeds - * 400 KB. For more information on batch restrictions see, http://docs.aws.amazon - * .com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html - *

    - * - * @see DynamoDbMapper#batchWrite(Iterable, Iterable) - */ - List batchSave(Iterable objectsToSave); - - /** - * Saves the objects given using one or more calls to the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. No version checks are - * performed, as required by the API. - *

    - * This method ignores any SaveBehavior set on the mapper, and always behaves as if - * SaveBehavior.CLOBBER was specified, as the DynamoDbClient.batchWriteItem() request does not - * support updating existing items. * - *

    - * This method fails to save the batch if the size of an individual object in the batch exceeds - * 400 KB. For more information on batch restrictions see, http://docs.aws.amazon - * .com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html - *

    - * - * @see DynamoDbMapper#batchWrite(Iterable, Iterable) - */ - List batchSave(Object... objectsToSave); - - /** - * Saves and deletes the objects given using one or more calls to the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. No version checks are - * performed, as required by the API. - *

    - * This method ignores any SaveBehavior set on the mapper, and always behaves as if - * SaveBehavior.CLOBBER was specified, as the DynamoDbClient.batchWriteItem() request does not - * support updating existing items. - *

    - * This method fails to save the batch if the size of an individual object in the batch exceeds - * 400 KB. For more information on batch restrictions see, http://docs.aws.amazon - * .com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html - *

    - *

    - * If one of the write requests is for a table that is not present, this method does not throw a - * ResourceNotFoundException but returns a FailedBatch which includes this exception and the - * unprocessed items. - *

    - * - * @see DynamoDbMapper#batchWrite(Iterable, Iterable) - */ - List batchWrite(Iterable objectsToWrite, Iterable objectsToDelete); - - /** - * Saves and deletes the objects given using one or more calls to the - * {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. Use mapper config to - * control the retry strategy when UnprocessedItems are returned by the BatchWriteItem API - *

    - * This method fails to save the batch if the size of an individual object in the batch exceeds - * 400 KB. For more information on batch restrictions see, http://docs.aws.amazon - * .com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html - *

    - *

    - * If one of the write requests is for a table that is not present, this method does not throw a - * ResourceNotFoundException but returns a FailedBatch which includes this exception and the - * unprocessed items. - *

    - * - * @param objectsToWrite - * A list of objects to save to DynamoDB. No version checks are performed, as - * required by the {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} API. - * @param objectsToDelete - * A list of objects to delete from DynamoDB. No version checks are performed, - * as required by the {@link DynamoDbClient#batchWriteItem(BatchWriteItemRequest)} - * API. - * @param config - * Only {@link DynamoDbMapperConfig#getTableNameOverride()} and - * {@link DynamoDbMapperConfig#batchWriteRetryStrategy()} are considered. If - * TableNameOverride is specified, all objects in the two parameter lists will be - * considered to belong to the given table override. In particular, this method - * always acts as if SaveBehavior.CLOBBER was specified regardless of the - * value of the config parameter. - * @return A list of failed batches which includes the unprocessed items and the exceptions - * causing the failure. - * @see DynamoDbMapperConfig#getTableNameOverride() - * @see DynamoDbMapperConfig#batchWriteRetryStrategy() - */ - List batchWrite(Iterable objectsToWrite, - Iterable objectsToDelete, - DynamoDbMapperConfig config); - - /** - * Retrieves multiple items from multiple tables using their primary keys. - * - * @see DynamoDbMapper#batchLoad(List, DynamoDbMapperConfig) - * @return A map of the loaded objects. Each key in the map is the name of a DynamoDB table. - * Each value in the map is a list of objects that have been loaded from that table. All - * objects for each table can be cast to the associated user defined type that is - * annotated as mapping that table. - * @throws DynamoDbMapper.BatchGetItemException if all the requested items are not processed - * within the maximum number of retries. - */ - Map> batchLoad(Iterable itemsToGet); - - /** - * Retrieves multiple items from multiple tables using their primary keys. - * - * @param itemsToGet - * Key objects, corresponding to the class to fetch, with their primary key values - * set. - * @param config - * Only {@link DynamoDbMapperConfig#getTableNameOverride()} and - * {@link DynamoDbMapperConfig#getConsistentRead()} are considered. - * @return A map of the loaded objects. Each key in the map is the name of a DynamoDB table. - * Each value in the map is a list of objects that have been loaded from that table. All - * objects for each table can be cast to the associated user defined type that is - * annotated as mapping that table. - * @throws DynamoDbMapper.BatchGetItemException if all the requested items are not processed - * within the maximum number of retries. - */ - Map> batchLoad(Iterable itemsToGet, DynamoDbMapperConfig config); - - /** - * Retrieves the attributes for multiple items from multiple tables using their primary keys. - * {@link DynamoDbClient#batchGetItem(BatchGetItemRequest)} API. - * - * @return A map of the loaded objects. Each key in the map is the name of a DynamoDB table. - * Each value in the map is a list of objects that have been loaded from that table. All - * objects for each table can be cast to the associated user defined type that is - * annotated as mapping that table. - * @throws DynamoDbMapper.BatchGetItemException if all the requested items are not processed - * within the maximum number of retries. - * @see #batchLoad(List, DynamoDbMapperConfig) - * @see #batchLoad(Map, DynamoDbMapperConfig) - */ - Map> batchLoad(Map, List> itemsToGet); - - /** - * Retrieves multiple items from multiple tables using their primary keys. Valid only for tables - * with a single hash key, or a single hash and range key. For other schemas, use - * {@link DynamoDbMapper#batchLoad(List, DynamoDbMapperConfig)} - * - * @param itemsToGet - * Map from class to load to list of primary key attributes. - * @param config - * Only {@link DynamoDbMapperConfig#getTableNameOverride()} and - * {@link DynamoDbMapperConfig#getConsistentRead()} are considered. - * @return A map of the loaded objects. Each key in the map is the name of a DynamoDB table. - * Each value in the map is a list of objects that have been loaded from that table. All - * objects for each table can be cast to the associated user defined type that is - * annotated as mapping that table. - * @throws DynamoDbMapper.BatchGetItemException if all the requested items are not processed - * within the maximum number of retries. - */ - Map> batchLoad(Map, List> itemsToGet, DynamoDbMapperConfig config); - - /** - * Scans through an Amazon DynamoDB table and returns the matching results as an unmodifiable - * list of instantiated objects, using the default configuration. - * - * @see DynamoDbMapper#scan(Class, DynamoDbScanExpression, DynamoDbMapperConfig) - */ - PaginatedScanList scan(Class clazz, DynamoDbScanExpression scanExpression); - - /** - * Scans through an Amazon DynamoDB table and returns the matching results as an unmodifiable - * list of instantiated objects. The table to scan is determined by looking at the annotations - * on the specified class, which declares where to store the object data in Amazon DynamoDB, and - * the scan expression parameter allows the caller to filter results and control how the scan is - * executed. - *

    - * Callers should be aware that the returned list is unmodifiable, and any attempts to modify - * the list will result in an UnsupportedOperationException. - *

    - * You can specify the pagination loading strategy for this scan operation. By default, the list - * returned is lazily loaded when possible. - * - * @param - * The type of the objects being returned. - * @param clazz - * The class annotated with DynamoDB annotations describing how to store the object - * data in Amazon DynamoDB. - * @param scanExpression - * Details on how to run the scan, including any filters to apply to limit results. - * @param config - * The configuration to use for this scan, which overrides the default provided at - * object construction. - * @return An unmodifiable list of the objects constructed from the results of the scan - * operation. - * @see PaginatedScanList - * @see PaginationLoadingStrategy - */ - PaginatedScanList scan(Class clazz, DynamoDbScanExpression scanExpression, DynamoDbMapperConfig config); - - /** - * Scans through an Amazon DynamoDB table on logically partitioned segments in parallel and - * returns the matching results in one unmodifiable list of instantiated objects, using the - * default configuration. - * - * @see DynamoDbMapper#parallelScan(Class, DynamoDbScanExpression, int, DynamoDbMapperConfig) - */ - PaginatedParallelScanList parallelScan(Class clazz, - DynamoDbScanExpression scanExpression, - int totalSegments); - - /** - * Scans through an Amazon DynamoDB table on logically partitioned segments in parallel. This - * method will create a thread pool of the specified size, and each thread will issue scan - * requests for its assigned segment, following the returned continuation token, until the end - * of its segment. Callers should be responsible for setting the appropriate number of total - * segments. More scan segments would result in better performance but more consumed capacity of - * the table. The results are returned in one unmodifiable list of instantiated objects. The - * table to scan is determined by looking at the annotations on the specified class, which - * declares where to store the object data in Amazon DynamoDB, and the scan expression parameter - * allows the caller to filter results and control how the scan is executed. - *

    - * Callers should be aware that the returned list is unmodifiable, and any attempts to modify - * the list will result in an UnsupportedOperationException. - *

    - * You can specify the pagination loading strategy for this parallel scan operation. By default, - * the list returned is lazily loaded when possible. - * - * @param - * The type of the objects being returned. - * @param clazz - * The class annotated with DynamoDB annotations describing how to store the object - * data in Amazon DynamoDB. - * @param scanExpression - * Details on how to run the scan, including any filters to apply to limit results. - * @param totalSegments - * Number of total parallel scan segments. Range: 1 - 4096 - * @param config - * The configuration to use for this scan, which overrides the default provided at - * object construction. - * @return An unmodifiable list of the objects constructed from the results of the scan - * operation. - * @see PaginatedParallelScanList - * @see PaginationLoadingStrategy - */ - PaginatedParallelScanList parallelScan(Class clazz, - DynamoDbScanExpression scanExpression, - int totalSegments, - DynamoDbMapperConfig config); - - /** - * Scans through an Amazon DynamoDB table and returns a single page of matching results. The - * table to scan is determined by looking at the annotations on the specified class, which - * declares where to store the object data in AWS DynamoDB, and the scan expression parameter - * allows the caller to filter results and control how the scan is executed. - * - * @param - * The type of the objects being returned. - * @param clazz - * The class annotated with DynamoDB annotations describing how to store the object - * data in Amazon DynamoDB. - * @param scanExpression - * Details on how to run the scan, including any filters to apply to limit results. - * @param config - * The configuration to use for this scan, which overrides the default provided at - * object construction. - */ - ScanResultPage scanPage(Class clazz, DynamoDbScanExpression scanExpression, DynamoDbMapperConfig config); - - /** - * Scans through an Amazon DynamoDB table and returns a single page of matching results. - * - * @see DynamoDbMapper#scanPage(Class, DynamoDbScanExpression, DynamoDbMapperConfig) - */ - ScanResultPage scanPage(Class clazz, DynamoDbScanExpression scanExpression); - - /** - * Queries an Amazon DynamoDB table and returns the matching results as an unmodifiable list of - * instantiated objects, using the default configuration. - * - * @see DynamoDbMapper#query(Class, DynamoDbQueryExpression, DynamoDbMapperConfig) - */ - PaginatedQueryList query(Class clazz, DynamoDbQueryExpression queryExpression); - - /** - * Queries an Amazon DynamoDB table and returns the matching results as an unmodifiable list of - * instantiated objects. The table to query is determined by looking at the annotations on the - * specified class, which declares where to store the object data in Amazon DynamoDB, and the - * query expression parameter allows the caller to filter results and control how the query is - * executed. - *

    - * When the query is on any local/global secondary index, callers should be aware that the - * returned object(s) will only contain item attributes that are projected into the index. All - * the other unprojected attributes will be saved as type default values. - *

    - * Callers should also be aware that the returned list is unmodifiable, and any attempts to - * modify the list will result in an UnsupportedOperationException. - *

    - * You can specify the pagination loading strategy for this query operation. By default, the - * list returned is lazily loaded when possible. - * - * @param - * The type of the objects being returned. - * @param clazz - * The class annotated with DynamoDB annotations describing how to store the object - * data in Amazon DynamoDB. - * @param queryExpression - * Details on how to run the query, including any conditions on the key values - * @param config - * The configuration to use for this query, which overrides the default provided at - * object construction. - * @return An unmodifiable list of the objects constructed from the results of the query - * operation. - * @see PaginatedQueryList - * @see PaginationLoadingStrategy - */ - PaginatedQueryList query(Class clazz, - DynamoDbQueryExpression queryExpression, - DynamoDbMapperConfig config); - - /** - * Queries an Amazon DynamoDB table and returns a single page of matching results. The table to - * query is determined by looking at the annotations on the specified class, which declares - * where to store the object data in Amazon DynamoDB, and the query expression parameter allows - * the caller to filter results and control how the query is executed. - * - * @see DynamoDbMapper#queryPage(Class, DynamoDbQueryExpression, DynamoDbMapperConfig) - */ - QueryResultPage queryPage(Class clazz, DynamoDbQueryExpression queryExpression); - - /** - * Queries an Amazon DynamoDB table and returns a single page of matching results. The table to - * query is determined by looking at the annotations on the specified class, which declares - * where to store the object data in Amazon DynamoDB, and the query expression parameter allows - * the caller to filter results and control how the query is executed. - * - * @param - * The type of the objects being returned. - * @param clazz - * The class annotated with DynamoDB annotations describing how to store the object - * data in AWS DynamoDB. - * @param queryExpression - * Details on how to run the query, including any conditions on the key values - * @param config - * The configuration to use for this query, which overrides the default provided at - * object construction. - */ - QueryResultPage queryPage(Class clazz, - DynamoDbQueryExpression queryExpression, - DynamoDbMapperConfig config); - - /** - * Evaluates the specified scan expression and returns the count of matching items, without - * returning any of the actual item data, using the default configuration. - * - * @see DynamoDbMapper#count(Class, DynamoDbScanExpression, DynamoDbMapperConfig) - */ - int count(Class clazz, DynamoDbScanExpression scanExpression); - - /** - * Evaluates the specified scan expression and returns the count of matching items, without - * returning any of the actual item data. - *

    - * This operation will scan your entire table, and can therefore be very expensive. Use with - * caution. - * - * @param clazz - * The class mapped to a DynamoDB table. - * @param scanExpression - * The parameters for running the scan. - * @param config - * The configuration to use for this scan, which overrides the default provided at - * object construction. - * @return The count of matching items, without returning any of the actual item data. - */ - int count(Class clazz, DynamoDbScanExpression scanExpression, DynamoDbMapperConfig config); - - /** - * Evaluates the specified query expression and returns the count of matching items, without - * returning any of the actual item data, using the default configuration. - * - * @see DynamoDbMapper#count(Class, DynamoDbQueryExpression, DynamoDbMapperConfig) - */ - int count(Class clazz, DynamoDbQueryExpression queryExpression); - - /** - * Evaluates the specified query expression and returns the count of matching items, without - * returning any of the actual item data. - * - * @param clazz - * The class mapped to a DynamoDB table. - * @param queryExpression - * The parameters for running the scan. - * @param config - * The mapper configuration to use for the query, which overrides the default - * provided at object construction. - * @return The count of matching items, without returning any of the actual item data. - */ - int count(Class clazz, DynamoDbQueryExpression queryExpression, DynamoDbMapperConfig config); - - /** - * Returns the underlying {@link S3ClientCache} for accessing S3. - */ - S3ClientCache s3ClientCache(); - - /** - * Creates an S3Link with the specified bucket name and key using the default S3 region. This - * method requires the mapper to have been initialized with the necessary credentials for - * accessing S3. - * - * @throws IllegalStateException - * if the mapper has not been constructed with the necessary S3 AWS credentials. - */ - S3Link createS3Link(String bucketName, String key); - - /** - * Creates an S3Link with the specified region, bucket name and key. This method requires the - * mapper to have been initialized with the necessary credentials for accessing S3. - * - * @throws IllegalStateException - * if the mapper has not been constructed with the necessary S3 AWS credentials. - */ - S3Link createS3Link(Region s3region, String bucketName, String key); - - /** - * Creates an S3Link with the specified region, bucket name and key. This method requires the - * mapper to have been initialized with the necessary credentials for accessing S3. - * - * @throws IllegalStateException - * if the mapper has not been constructed with the necessary S3 AWS credentials. - */ - S3Link createS3Link(String s3region, String bucketName, String key); - - /** - * Parse the given POJO class and return the CreateTableRequest for the DynamoDB table it - * represents. Note that the returned request does not include the required - * ProvisionedThroughput parameters for the primary table and the GSIs, and that all secondary - * indexes are initialized with the default projection type - KEY_ONLY. - */ - CreateTableRequest generateCreateTableRequest(Class clazz); - - - /** - * Parse the given POJO class and return the DeleteTableRequest for the DynamoDB table it - * represents. - */ - DeleteTableRequest generateDeleteTableRequest(Class clazz); - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/IncompatibleSubclassTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/IncompatibleSubclassTest.java deleted file mode 100644 index f60ef765494e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/IncompatibleSubclassTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Map; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * Verify that we fail fast in case of incompatible subclasses that try to - * override the (now-removed) transformAttributes method. - */ -public class IncompatibleSubclassTest { - - @Test - public void testCompatibleSubclass() { - // Doesn't try to override one of the deprecated/removed - // transformAttributes methods; should be fine. - new CompatibleDynamoDbMapper(); - } - - @Test(expected = IllegalStateException.class) - public void testIncompatibleSubclass1() { - // "Overrides" transformAttributes(Class, Map); should fail fast. - new IncompatibleDynamoDbMapper1(); - } - - @Test(expected = IllegalStateException.class) - public void testIncompatibleSubclass2() { - // "Overrides" transformAttributes(String, String, Map); should fail - // fast. - new IncompatibleDynamoDbMapper2(); - } - - private static class CompatibleDynamoDbMapper extends DynamoDbMapper { - - public CompatibleDynamoDbMapper() { - super(null); - } - - protected void transformAttributes(boolean innocuous) { - } - } - - private static class IncompatibleDynamoDbMapper1 extends DynamoDbMapper { - - public IncompatibleDynamoDbMapper1() { - super(null); - } - - protected Map transformAttributes( - Class clazz, - Map attributeValues) { - - return null; - } - } - - private static class IncompatibleDynamoDbMapper2 extends DynamoDbMapper { - - public IncompatibleDynamoDbMapper2() { - super(null); - } - - protected Map transformAttributes( - String hashKey, - String rangeKey, - Map attributeValues) { - - return null; - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ItemConverter.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ItemConverter.java deleted file mode 100644 index d866365ec657..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ItemConverter.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * The concrete realization of a strategy for converting between Java objects - * and DynamoDB AttributeValues. Typically created by a - * {@link ConversionSchema}. - */ -public interface ItemConverter { - /** - * Returns the metadata (e.g. name, type) of the DynamoDB attribute that the - * return value of the given getter will be converted to. - * - * @param getter the getter method to inspect - * @return the metadata of the DynamoDB attribute that the result of the - * getter will be converted to - */ - DynamoDbMapperFieldModel getFieldModel(Method getter); - - /** - * Converts a Java object into a DynamoDB AttributeValue. Potentially able - * to handle both scalar and complex types. - * - * @param getter the getter that returned the value to be converted - * @param value the value to be converted - * @return the converted AttributeValue - */ - AttributeValue convert(Method getter, Object value); - - /** - * Converts an appropriately-annotated POJO into a Map of AttributeValues. - * - * @param value the POJO to convert - * @return the resulting map of attribute values - */ - Map convert(Object value); - - /** - * Reverses the {@link #convert(Method, Object)} method, turning a - * DynamoDB AttributeValue back into a Java object suitable for passing - * to the given setter. - * - * @param getter the getter for the value to be unconverted - * @param setter the setter for the value to be unconverted - * @param value the attribute value to be unconverted - * @return the unconverted Java object - */ - Object unconvert(Method getter, Method setter, AttributeValue value); - - /** - * Reverses the {@link #convert(Object)} method, turning a map of attribute - * values back into a POJO of the given class. - * - * @param the compile-time type of the object to create - * @param clazz the runtime type of the object to create - * @param values the the map of attribute values to unconvert - * @return the unconverted POJO - */ - T unconvert(Class clazz, Map values); -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonMarshaller.java deleted file mode 100644 index dd9da70b6f4d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/JsonMarshaller.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static software.amazon.awssdk.core.internal.util.ThrowableUtils.failure; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; - -/** - * A simple JSON marshaller that uses the Jackson JSON processor. It shares all limitations of that - * library. For more information about Jackson, see: http://wiki.fasterxml.com/JacksonHome - * - * @deprecated Replaced by {@link DynamoDbTypeConvertedJson} - */ -@Deprecated -public class JsonMarshaller implements DynamoDbMarshaller { - - private static final ObjectMapper MAPPER = new ObjectMapper(); - private static final ObjectWriter WRITER = MAPPER.writer(); - - /** - * The value type. - */ - private final Class valueType; - - /** - * Constructs the JSON marshaller instance. - * @param valueType The value type (for generic type erasure). - */ - public JsonMarshaller(final Class valueType) { - this.valueType = valueType; - } - - /** - * Constructs the JSON marshaller instance. - */ - public JsonMarshaller() { - this(null); - } - - /** - * Gets the value type. - * @return The value type. - */ - protected final Class valueType() { - return this.valueType; - } - - @Override - public String marshall(T obj) { - - try { - return WRITER.writeValueAsString(obj); - } catch (JsonProcessingException e) { - throw failure(e, - "Unable to marshall the instance of " + obj.getClass() - + "into a string"); - } - } - - @Override - public T unmarshall(Class clazz, String json) { - try { - return MAPPER.readValue(json, (valueType() == null ? clazz : valueType())); - } catch (Exception e) { - throw failure(e, "Unable to unmarshall the string " + json - + "into " + clazz); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/KeyPair.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/KeyPair.java deleted file mode 100644 index b1afbfee7de3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/KeyPair.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -public class KeyPair { - private Object hashKey; - private Object rangeKey; - - public KeyPair withHashKey(Object hashkey) { - this.hashKey = hashkey; - return this; - } - - public KeyPair withRangeKey(Object rangeKey) { - this.rangeKey = rangeKey; - return this; - } - - public Object getHashKey() { - return this.hashKey; - } - - public void setHashKey(Object hashKey) { - this.hashKey = hashKey; - } - - public Object getRangeKey() { - return this.rangeKey; - } - - public void setRangeKey(Object rangeKey) { - this.rangeKey = rangeKey; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedList.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedList.java deleted file mode 100644 index 249cf1e68a33..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedList.java +++ /dev/null @@ -1,528 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.PaginationLoadingStrategy; - -/** - * Unmodifiable list supporting paginated result sets from Amazon DynamoDB. - *

    - * Pages of results are fetched lazily from DynamoDB as they are needed. Some - * methods, such as {@link PaginatedList#size()} and - * {@link PaginatedList#toArray()}, require fetching the entire result set - * eagerly. See the javadoc of individual methods for details on which are lazy. - * - * @param - * The domain object type stored in this list. - */ -public abstract class PaginatedList implements List { - - private static final String UNMODIFIABLE_MESSAGE = "This is an unmodifiable list"; - - private static final String ITERATION_ONLY_UNSUPPORTED_OPERATION_MESSAGE = - " is not supported when using ITERATION_ONLY configuration."; - - /** - * Reference to the DynamoDB mapper for marshalling DynamoDB attributes back - * into objects - */ - protected final DynamoDbMapper mapper; - - /** - * The class annotated with DynamoDB tags declaring how to load/store - * objects into DynamoDB - */ - protected final Class clazz; - - /** The client for working with DynamoDB. */ - protected final DynamoDbClient dynamo; - - /** Tracks if all results have been loaded yet or not. */ - protected boolean allResultsLoaded = false; - - /** - * All currently loaded results for this list. - * - * In ITERATION_ONLY mode, this list will at most keep one page of the - * loaded results, and all previous results will be cleared from the memory. - */ - protected final List allResults; - /** Lazily loaded next results waiting to be added into allResults. */ - protected final List nextResults = new LinkedList(); - /** The pagination loading strategy for this paginated list **/ - private final PaginationLoadingStrategy paginationLoadingStrategy; - /** - * Keeps track on whether an iterator of the list has been retrieved. - * Only updated and checked when the list is in ITERATION_ONLY mode. - */ - private boolean iterationstarted = false; - - /** - * Constructs a PaginatedList instance using the default PaginationLoadingStrategy - */ - public PaginatedList(DynamoDbMapper mapper, Class clazz, DynamoDbClient dynamo) { - this(mapper, clazz, dynamo, null); - } - - /** - * Constructs a PaginatedList instance. - * - * @param mapper - * The mapper for marshalling DynamoDB attributes into objects. - * @param clazz - * The class of the annotated model. - * @param dynamo - * The DynamoDB client for making low-level request calls. - * @param paginationLoadingStrategy - * The strategy used for loading paginated results. Caller has to - * explicitly set this parameter, since the DynamoDBMapperConfig - * set in the mapper is not accessible here. If null value is - * provided, LAZY_LOADING will be set by default. - */ - public PaginatedList(DynamoDbMapper mapper, Class clazz, DynamoDbClient dynamo, - PaginationLoadingStrategy paginationLoadingStrategy) { - this.mapper = mapper; - this.clazz = clazz; - this.dynamo = dynamo; - this.paginationLoadingStrategy = paginationLoadingStrategy == null ? - PaginationLoadingStrategy.LAZY_LOADING : paginationLoadingStrategy; - - this.allResults = new ArrayList(); - - // Ideally, we should eagerly load all results here as soon as EAGER_LOADING is configured. - // But the implementation of loadAllResults() relies on a fully initialized sub-class object. - // So we have to do this in each sub-class constructor. - } - - /** - * Eagerly loads all results for this list. - *

    - * Not supported in ITERATION_ONLY mode. - *

    - */ - public synchronized void loadAllResults() { - checkUnsupportedOperationForIterationOnlyMode("loadAllResults()"); - - if (allResultsLoaded) { - return; - } - - while (nextResultsAvailable()) { - // Keep all loaded results - moveNextResults(false); - } - - allResultsLoaded = true; - } - - /** - * Returns whether there are more results available not yet included in the - * allResults member field. These could already have been fetched and are - * sitting in the nextResults buffer, or they could be fetched from the - * service opportunistically at the time this method is called. A return - * value of true guarantees that nextResults is non-empty. - */ - private boolean nextResultsAvailable() { - return !nextResults.isEmpty() || loadNextResults(); - } - - /** - * Attempts to load the next batch of results, if there are any, into the - * nextResults buffer. Returns whether there were any results to load. A - * return value of true guarantees that nextResults had items added to it. - */ - private synchronized boolean loadNextResults() { - if (atEndOfResults()) { - return false; - } - - do { - nextResults.addAll(fetchNextPage()); - } while (!atEndOfResults() && nextResults.isEmpty()); - - return !nextResults.isEmpty(); - } - - /** - * Moves the contents of the nextResults buffer into allResults and resets - * the buffer. - * - * @param clearPreviousResults - * Whether it should clear previous results in allResults field. - */ - private void moveNextResults(boolean clearPreviousResults) { - if (clearPreviousResults) { - allResults.clear(); - } - allResults.addAll(nextResults); - nextResults.clear(); - } - - /** - * Fetches the next page of results (which may be empty) and returns any - * items found. - */ - protected abstract List fetchNextPage(); - - /** - * Returns whether we have reached the end of the result set. - */ - protected abstract boolean atEndOfResults(); - - /** - * Returns an iterator over this list that lazily initializes results as - * necessary. - *

    - * If it configured with ITERARTION_ONLY mode, then the iterator - * could be only retrieved once, and any previously loaded results will be - * cleared in the memory during the iteration. - *

    - */ - @Override - public Iterator iterator() { - return new PaginatedListIterator(paginationLoadingStrategy == PaginationLoadingStrategy.ITERATION_ONLY); - } - - /** - * Returns whether the collection is empty. At most one (non-empty) page of - * results is loaded to make the check. - *

    - * Not supported in ITERATION_ONLY mode. - *

    - */ - @Override - public boolean isEmpty() { - checkUnsupportedOperationForIterationOnlyMode("isEmpty()"); - - return !iterator().hasNext(); - } - - /** - * Returns the Nth element of the list. Results are loaded until N elements - * are present, if necessary. - *

    - * Not supported in ITERATION_ONLY mode. - *

    - */ - @Override - public T get(int n) { - checkUnsupportedOperationForIterationOnlyMode("get(int n)"); - - while (allResults.size() <= n && nextResultsAvailable()) { - moveNextResults(false); - } - - return allResults.get(n); - } - - /** - * Returns whether the collection contains the given element. Results are - * loaded and checked incrementally until a match is found or the end of the - * result set is reached. - *

    - * Not supported in ITERATION_ONLY mode. - *

    - */ - @Override - public boolean contains(Object arg0) { - checkUnsupportedOperationForIterationOnlyMode("contains(Object arg0)"); - - if (allResults.contains(arg0)) { - return true; - } - - while (nextResultsAvailable()) { - boolean found = nextResults.contains(arg0); - moveNextResults(false); - if (found) { - return true; - } - } - - return false; - } - - /** - * Returns a sub-list in the range specified, loading more results as - * necessary. - *

    - * Not supported in ITERATION_ONLY mode. - *

    - */ - @Override - public List subList(int arg0, int arg1) { - checkUnsupportedOperationForIterationOnlyMode("subList(int arg0, int arg1)"); - - while (allResults.size() < arg1 && nextResultsAvailable()) { - moveNextResults(false); - } - - return Collections.unmodifiableList(allResults.subList(arg0, arg1)); - } - - /** - * Returns the first index of the object given in the list. Additional - * results are loaded incrementally as necessary. - *

    - * Not supported in ITERATION_ONLY mode. - *

    - */ - @Override - public int indexOf(Object arg0) { - checkUnsupportedOperationForIterationOnlyMode("indexOf(Object org0)"); - - int indexOf = allResults.indexOf(arg0); - if (indexOf >= 0) { - return indexOf; - } - - while (nextResultsAvailable()) { - indexOf = nextResults.indexOf(arg0); - int size = allResults.size(); - moveNextResults(false); - if (indexOf >= 0) { - return indexOf + size; - } - } - - return -1; - } - - @Override - public int size() { - loadAllResults(); - return allResults.size(); - } - - // Operations requiring the entire result set - - @Override - public boolean containsAll(Collection arg0) { - loadAllResults(); - return allResults.containsAll(arg0); - } - - @Override - public int lastIndexOf(Object arg0) { - loadAllResults(); - return allResults.lastIndexOf(arg0); - } - - @Override - public Object[] toArray() { - loadAllResults(); - return allResults.toArray(); - } - - @Override - public X[] toArray(X[] a) { - loadAllResults(); - return allResults.toArray(a); - } - - @Override - public ListIterator listIterator() { - throw new UnsupportedOperationException("ListIterators are not supported for this list"); - } - - // Unsupported Operations - - @Override - public ListIterator listIterator(int arg0) { - throw new UnsupportedOperationException("ListIterators are not supported for this list"); - } - - @Override - public boolean remove(Object arg0) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public T remove(int arg0) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public boolean removeAll(Collection arg0) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public boolean retainAll(Collection arg0) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public T set(int arg0, T arg1) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public boolean add(T arg0) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public void add(int arg0, T arg1) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public boolean addAll(Collection arg0) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public boolean addAll(int arg0, Collection arg1) { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - private void checkUnsupportedOperationForIterationOnlyMode(String methodSignature) { - if (this.paginationLoadingStrategy == PaginationLoadingStrategy.ITERATION_ONLY) { - throw new UnsupportedOperationException(methodSignature + ITERATION_ONLY_UNSUPPORTED_OPERATION_MESSAGE); - } - } - - private class PaginatedListIterator implements Iterator { - /** - * Whether this iterator is constructed by a PaginatedList in - * ITERATION_ONLY mode. - */ - private final boolean iterationOnly; - - /** - * A hard copy of the allResults list to prevent - * ConcurrentModificationExceptions. - * Only needed when the list is not in ITERNATION_ONLY mode. - */ - private final List allResultsCopy; - - private Iterator innerIterator; - - private int pos = 0; - - PaginatedListIterator(boolean iterationOnly) { - this.iterationOnly = iterationOnly; - - if (iterationOnly) { - synchronized (PaginatedList.this) { - if (iterationstarted) { - throw new UnsupportedOperationException("The list could only be iterated once in ITERATION_ONLY mode."); - } - iterationstarted = true; - } - - allResultsCopy = null; // not needed for ITERATION_ONLY mode - innerIterator = allResults.iterator(); - } else { - /* - * We make a copy of the allResults list to iterate over in order to - * avoid ConcurrentModificationExceptions caused by other methods - * loading more results into the list while someone iterates over it. - * This is a more severe problem than it might seem, because even - * innocuous-seeming operations such as contains() can modify the - * underlying result set. - */ - allResultsCopy = new ArrayList(); - allResultsCopy.addAll(allResults); - innerIterator = allResultsCopy.iterator(); - } - } - - @Override - public boolean hasNext() { - return innerIterator.hasNext() || shouldSyncWithAllResultsList() || - nextResultsAvailable(); - } - - /** - * If we aren't in ITERATION_ONLY mode then allResults is the authoriative source of - * results. If it's size has increased since this iterator was last synched with it then we - * have more results to process and need to re-sync allResultsCopy with allResults. - * - * @return True if more results are available in allResults then what we have currently - * snapshoted in the iterator, false otherwise. - */ - private boolean shouldSyncWithAllResultsList() { - return !iterationOnly && allResults.size() > allResultsCopy.size(); - } - - @Override - public T next() { - if (!innerIterator.hasNext()) { - /* - * We need to immediately fetch more results from the service, - * if - * -- it's in ITERATION_ONLY mode (which means innerIterator - * is always pointing at the "real" list of loaded results) - * OR it's not in ITERATION_ONLY and our private copy of the - * result list is already up to date with the full one. - */ - if (iterationOnly - || allResults.size() == allResultsCopy.size()) { - if (!nextResultsAvailable()) { - throw new NoSuchElementException(); - } - /* Clear previous results if it's in ITERATION_ONLY mode. */ - boolean clearPreviousResults = iterationOnly; - moveNextResults(clearPreviousResults); - } - - if (iterationOnly) { - /* - * allResults has been replaced with the latest page of results. - */ - innerIterator = allResults.iterator(); - } else { - /* - * Update our private results copy, and then update the inner iterator - */ - if (allResults.size() > allResultsCopy.size()) { - allResultsCopy.addAll(allResults.subList(allResultsCopy.size(), - allResults.size())); - } - - innerIterator = allResultsCopy.listIterator(pos); - } - } - - pos++; - return innerIterator.next(); - } - - @Override - public void remove() { - throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE); - } - - } - - ; -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedParallelScanList.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedParallelScanList.java deleted file mode 100644 index 41638d43a9d3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedParallelScanList.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.LinkedList; -import java.util.List; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.PaginationLoadingStrategy; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -/** - * Implementation of the List interface that represents the results from a parallel scan - * in AWS DynamoDB. Paginated results are loaded on demand when the user - * executes an operation that requires them. Some operations, such as size(), - * must fetch the entire list, but results are lazily fetched page by page when - * possible. - *

    - * This is an unmodifiable list, so callers should not invoke any operations - * that modify this list, otherwise they will throw an - * UnsupportedOperationException. - * - * @param - * The type of objects held in this list. - * @see PaginatedList - */ -public class PaginatedParallelScanList extends PaginatedList { - - /** The current parallel scan task which contains all the information about the scan request. */ - private final ParallelScanTask parallelScanTask; - - private final DynamoDbMapperConfig config; - - public PaginatedParallelScanList( - DynamoDbMapper mapper, - Class clazz, - DynamoDbClient dynamo, - ParallelScanTask parallelScanTask, - PaginationLoadingStrategy paginationLoadingStrategy, - DynamoDbMapperConfig config) { - super(mapper, clazz, dynamo, paginationLoadingStrategy); - - this.parallelScanTask = parallelScanTask; - this.config = config; - - // Marshall the first batch of results in all Results - allResults.addAll(marshalParallelScanResponsesIntoObjects(parallelScanTask.nextBatchOfScanResponses())); - - // If the results should be eagerly loaded at once - if (paginationLoadingStrategy == PaginationLoadingStrategy.EAGER_LOADING) { - loadAllResults(); - } - } - - @Override - protected boolean atEndOfResults() { - return parallelScanTask.isAllSegmentScanFinished(); - } - - @Override - protected List fetchNextPage() { - return marshalParallelScanResponsesIntoObjects(parallelScanTask.nextBatchOfScanResponses()); - } - - private List marshalParallelScanResponsesIntoObjects(List scanResults) { - List allItems = new LinkedList(); - for (ScanResponse scanResult : scanResults) { - if (null != scanResult) { - allItems.addAll(mapper.marshallIntoObjects( - mapper.toParameters( - scanResult.items(), - clazz, - parallelScanTask.getTableName(), - config))); - } - } - return allItems; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedQueryList.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedQueryList.java deleted file mode 100644 index 9b2cf6a9e25d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedQueryList.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.List; - -import software.amazon.awssdk.core.util.SdkAutoConstructMap; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.PaginationLoadingStrategy; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - -/** - * Implementation of the List interface that represents the results from a query - * in AWS DynamoDB. Paginated results are loaded on demand when the user - * executes an operation that requires them. Some operations, such as size(), - * must fetch the entire list, but results are lazily fetched page by page when - * possible. - *

    - * This is an unmodifiable list, so callers should not invoke any operations - * that modify this list, otherwise they will throw an - * UnsupportedOperationException. - * - * @param - * The type of objects held in this list. - * @see PaginatedList - */ -public class PaginatedQueryList extends PaginatedList { - - /** The current query request. */ - private QueryRequest queryRequest; - - private final DynamoDbMapperConfig config; - - /** The current results for the last executed query operation. */ - private QueryResponse queryResult; - - public PaginatedQueryList( - DynamoDbMapper mapper, - Class clazz, - DynamoDbClient dynamo, - QueryRequest queryRequest, - QueryResponse queryResult, - PaginationLoadingStrategy paginationLoadingStrategy, - DynamoDbMapperConfig config) { - super(mapper, clazz, dynamo, paginationLoadingStrategy); - - this.queryRequest = queryRequest; - this.queryResult = queryResult; - this.config = config; - - - allResults.addAll(mapper.marshallIntoObjects( - mapper.toParameters( - queryResult.items(), - clazz, - queryRequest.tableName(), - config))); - - // If the results should be eagerly loaded at once - if (paginationLoadingStrategy == PaginationLoadingStrategy.EAGER_LOADING) { - loadAllResults(); - } - } - - @Override - protected synchronized boolean atEndOfResults() { - return queryResult.lastEvaluatedKey() instanceof SdkAutoConstructMap; - } - - @Override - protected synchronized List fetchNextPage() { - queryRequest = queryRequest.toBuilder().exclusiveStartKey(queryResult.lastEvaluatedKey()).build(); - queryResult = dynamo.query(DynamoDbMapper.applyUserAgent(queryRequest)); - return mapper.marshallIntoObjects(mapper.toParameters( - queryResult.items(), - clazz, - queryRequest.tableName(), - config)); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedScanList.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedScanList.java deleted file mode 100644 index 24d6a615389a..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedScanList.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.List; - -import software.amazon.awssdk.core.util.SdkAutoConstructMap; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig.PaginationLoadingStrategy; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -/** - * Implementation of the List interface that represents the results from a scan - * in AWS DynamoDB. Paginated results are loaded on demand when the user - * executes an operation that requires them. Some operations, such as size(), - * must fetch the entire list, but results are lazily fetched page by page when - * possible. - *

    - * This is an unmodifiable list, so callers should not invoke any operations - * that modify this list, otherwise they will throw an - * UnsupportedOperationException. - * - * @param - * The type of objects held in this list. - * @see PaginatedList - */ -public class PaginatedScanList extends PaginatedList { - - /** The current scan request. */ - private ScanRequest scanRequest; - - private final DynamoDbMapperConfig config; - - /** The current results for the last executed scan operation. */ - private ScanResponse scanResult; - - public PaginatedScanList( - DynamoDbMapper mapper, - Class clazz, - DynamoDbClient dynamo, - ScanRequest scanRequest, - ScanResponse scanResult, - PaginationLoadingStrategy paginationLoadingStrategy, - DynamoDbMapperConfig config) { - super(mapper, clazz, dynamo, paginationLoadingStrategy); - - this.scanRequest = scanRequest; - this.scanResult = scanResult; - this.config = config; - - allResults.addAll(mapper.marshallIntoObjects( - mapper.toParameters( - scanResult.items(), - clazz, - scanRequest.tableName(), - config))); - - // If the results should be eagerly loaded at once - if (paginationLoadingStrategy == PaginationLoadingStrategy.EAGER_LOADING) { - loadAllResults(); - } - } - - @Override - protected synchronized boolean atEndOfResults() { - return scanResult.lastEvaluatedKey() instanceof SdkAutoConstructMap; - } - - @Override - protected synchronized List fetchNextPage() { - scanRequest = scanRequest.toBuilder().exclusiveStartKey(scanResult.lastEvaluatedKey()).build(); - scanResult = dynamo.scan(DynamoDbMapper.applyUserAgent(scanRequest)); - return mapper.marshallIntoObjects(mapper.toParameters( - scanResult.items(), - clazz, - scanRequest.tableName(), - config)); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedScanTaskTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedScanTaskTest.java deleted file mode 100644 index 6740d6b59b8e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/PaginatedScanTaskTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.argThat; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentMatcher; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughputExceededException; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -@RunWith(MockitoJUnitRunner.class) -public class PaginatedScanTaskTest { - - private static final String TABLE_NAME = "FooTable"; - - private static final int TOTAL_SEGMENTS = 5; - - private ParallelScanTask parallelScanTask; - - private ExecutorService executorService; - - @Mock - private DynamoDbClient dynamoDB; - - /** - * Custom matcher to match argument based on it's segment number - * - * @param segmentNumber Segment number to match for this stub. - * @return Stubbed argument matcher - */ - private static ScanRequest isSegmentNumber(int segmentNumber) { - return argThat(new SegmentArgumentMatcher(segmentNumber)); - } - - @Before - public void setup() { - executorService = Executors.newSingleThreadExecutor(); - parallelScanTask = new ParallelScanTask(dynamoDB, createScanRequests(), executorService); - } - - /** - * A failed segment makes the scan task unusable and will always rethrow the same exception. In - * this case it makes sense to shutdown the executor so that applications can shutdown faster. A - * future enhancement could be to either retry failed segments, explicitly resume a failed scan, - * or include metadata in the thrown exception about the state of the scan at the time it was - * aborted. See PR #624 and Issue #624 for more details. - */ - @Test - public void segmentFailsToScan_ExecutorServiceIsShutdown() throws InterruptedException { - stubsuccessfulScan(0); - stubsuccessfulScan(1); - when(dynamoDB.scan(isSegmentNumber(2))) - .thenThrow(ProvisionedThroughputExceededException.builder().message("Slow Down!").build()); - stubsuccessfulScan(3); - stubsuccessfulScan(4); - - try { - parallelScanTask.nextBatchOfScanResponses(); - fail("Expected ProvisionedThroughputExceededException"); - } catch (ProvisionedThroughputExceededException expected) { - // Ignored or expected. - } - - executorService.awaitTermination(10, TimeUnit.SECONDS); - assertTrue(executorService.isShutdown()); - } - - /** - * Stub a successful scan of a segment with a precanned item to return. - * - * @param segmentNumber Segment to stub. - */ - private void stubsuccessfulScan(int segmentNumber) { - when(dynamoDB.scan(isSegmentNumber(segmentNumber))) - .thenReturn(ScanResponse.builder().items(generateItems()).build()); - } - - private Map generateItems() { - final int numItems = 10; - Map items = new HashMap(numItems); - for (int i = 0; i < numItems; i++) { - items.put(UUID.randomUUID().toString(), AttributeValue.builder().s("foo").build()); - } - return items; - } - - private List createScanRequests() { - final List scanRequests = new ArrayList(TOTAL_SEGMENTS); - for (int i = 0; i < TOTAL_SEGMENTS; i++) { - scanRequests.add(createScanRequest(i)); - } - return scanRequests; - } - - private ScanRequest createScanRequest(int segmentNumber) { - return ScanRequest.builder() - .tableName(TABLE_NAME) - .segment(segmentNumber) - .totalSegments(TOTAL_SEGMENTS) - .build(); - } - - /** - * Custom argument matcher to match a {@link ScanRequest} on the segment number. - */ - private static class SegmentArgumentMatcher extends ArgumentMatcher { - - private final int matchingSegmentNumber; - - private SegmentArgumentMatcher(int matchingSegmentNumber) { - this.matchingSegmentNumber = matchingSegmentNumber; - } - - @Override - public boolean matches(Object argument) { - if (!(argument instanceof ScanRequest)) { - return false; - } - return matchingSegmentNumber == ((ScanRequest) argument).segment(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ParallelScanTask.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ParallelScanTask.java deleted file mode 100644 index 27ca9b46af00..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ParallelScanTask.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import software.amazon.awssdk.annotations.SdkTestInternalApi; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.ScanRequest; -import software.amazon.awssdk.services.dynamodb.model.ScanResponse; - -public class ParallelScanTask { - - /** - * The list of hard copies of ScanRequest with different segment number. - */ - private final List parallelScanRequests; - - private final int totalSegments; - - /** - * Cache all the future tasks, so that we can extract the exception when - * we see failed segment scan. - */ - private final List> segmentScanFutureTasks; - - /** - * Cache all the most recent ScanResponse on each segment. - */ - private final List segmentScanResponses; - - /** - * The current state of the scan on each segment. - * Used as the monitor for synchronization. - */ - private final List segmentScanstates; - private final DynamoDbClient dynamo; - private ExecutorService executorService; - - @Deprecated - public ParallelScanTask(DynamoDbMapper mapper, DynamoDbClient dynamo, List parallelScanRequests) { - this(dynamo, parallelScanRequests); - } - - ParallelScanTask(DynamoDbClient dynamo, List parallelScanRequests) { - this(dynamo, parallelScanRequests, Executors.newCachedThreadPool()); - } - - @SdkTestInternalApi - ParallelScanTask(DynamoDbClient dynamo, List parallelScanRequests, - ExecutorService executorService) { - this.dynamo = dynamo; - this.parallelScanRequests = parallelScanRequests; - this.totalSegments = parallelScanRequests.size(); - this.executorService = executorService; - - // Create synchronized views of the list to guarantee any changes are visible across all threads. - segmentScanFutureTasks = Collections - .synchronizedList(new ArrayList>(totalSegments)); - segmentScanResponses = Collections.synchronizedList(new ArrayList(totalSegments)); - segmentScanstates = Collections - .synchronizedList(new ArrayList(totalSegments)); - - initSegmentScanstates(); - } - - String getTableName() { - return parallelScanRequests.get(0).tableName(); - } - - public boolean isAllSegmentScanFinished() { - synchronized (segmentScanstates) { - for (int segment = 0; segment < totalSegments; segment++) { - if (segmentScanstates.get(segment) != SegmentScanstate.SegmentScanCompleted) { - return false; - } - } - // Shut down if all data have been scanned and loaded. - executorService.shutdown(); - return true; - } - } - - public List nextBatchOfScanResponses() throws SdkClientException { - /** - * Kick-off all the parallel scan tasks. - */ - startScanNextPages(); - /** - * Wait till all the tasks have finished. - */ - synchronized (segmentScanstates) { - while (segmentScanstates.contains(SegmentScanstate.Waiting) - || segmentScanstates.contains(SegmentScanstate.Scanning)) { - try { - segmentScanstates.wait(); - } catch (InterruptedException ie) { - throw SdkClientException.builder() - .message("Parallel scan interrupted by other thread.") - .cause(ie) - .build(); - } - } - /** - * Keep the lock on segmentScanstates until all the cached results are marshaled and returned. - */ - return marshalParallelScanResponses(); - } - - } - - private void startScanNextPages() { - for (int segment = 0; segment < totalSegments; segment++) { - final int currentSegment = segment; - final SegmentScanstate currentSegmentState = segmentScanstates.get(currentSegment); - /** - * Assert: Should never see any task in state of "Scanning" when starting a new batch. - */ - if (currentSegmentState == SegmentScanstate.Scanning) { - - throw SdkClientException.builder() - .message("Should never see a 'Scanning' state when starting parallel scans.") - .build(); - - } else if (currentSegmentState == SegmentScanstate.Failed || - currentSegmentState == SegmentScanstate.SegmentScanCompleted) { - /** - * Skip any failed or completed segment, and clear the corresponding cached result. - */ - segmentScanResponses.set(currentSegment, null); - continue; - } else { - /** - * Otherwise, submit a new future task and save it in segmentScanFutureTasks. - */ - // Update the state to "Scanning" and notify any waiting thread. - synchronized (segmentScanstates) { - segmentScanstates.set(currentSegment, SegmentScanstate.Scanning); - segmentScanstates.notifyAll(); - } - Future futureTask = executorService.submit(() -> { - try { - if (currentSegmentState == SegmentScanstate.HasNextPage) { - return scanNextPageOfSegment(currentSegment, true); - } else if (currentSegmentState == SegmentScanstate.Waiting) { - return scanNextPageOfSegment(currentSegment, false); - } else { - throw SdkClientException.builder().message("Should not start a new future task").build(); - } - } catch (Exception e) { - synchronized (segmentScanstates) { - segmentScanstates.set(currentSegment, SegmentScanstate.Failed); - segmentScanstates.notifyAll(); - executorService.shutdown(); - } - throw e; - } - }); - // Cache the future task (for getting the Exceptions in the working thread). - segmentScanFutureTasks.set(currentSegment, futureTask); - } - } - } - - private List marshalParallelScanResponses() { - List scanResults = new LinkedList(); - for (int segment = 0; segment < totalSegments; segment++) { - SegmentScanstate currentSegmentState = segmentScanstates.get(segment); - /** - * Rethrow the exception from any failed segment scan. - */ - if (currentSegmentState == SegmentScanstate.Failed) { - try { - segmentScanFutureTasks.get(segment).get(); - throw SdkClientException.builder().message("No Exception found in the failed scan task.").build(); - } catch (ExecutionException ee) { - Throwable cause = ee.getCause(); - if (cause instanceof SdkServiceException) { - throw (SdkServiceException) cause; - } else { - throw SdkClientException.builder() - .message("Internal error during the scan on segment #" + segment + ".") - .build(); - } - } catch (Exception e) { - throw SdkClientException.builder() - .message("Error during the scan on segment #" + segment + ".") - .cause(e) - .build(); - } - } else if (currentSegmentState == SegmentScanstate.HasNextPage || - currentSegmentState == SegmentScanstate.SegmentScanCompleted) { - /** - * Get the ScanResponse from cache if the segment scan has finished. - */ - ScanResponse scanResult = segmentScanResponses.get(segment); - scanResults.add(scanResult); - } else if (currentSegmentState == SegmentScanstate.Waiting - || currentSegmentState == SegmentScanstate.Scanning) { - throw SdkClientException.builder() - .message("Should never see a 'Scanning' or 'Waiting' state when marshalling parallel " + - "scan results.") - .build(); - } - } - return scanResults; - } - - private ScanResponse scanNextPageOfSegment(int currentSegment, boolean checkLastEvaluatedKey) { - ScanRequest segmentScanRequest = parallelScanRequests.get(currentSegment); - if (checkLastEvaluatedKey) { - ScanResponse lastScanResult = segmentScanResponses.get(currentSegment); - segmentScanRequest = segmentScanRequest.toBuilder().exclusiveStartKey(lastScanResult.lastEvaluatedKey()).build(); - } else { - segmentScanRequest = segmentScanRequest.toBuilder().exclusiveStartKey(null).build(); - } - ScanResponse scanResult = dynamo.scan(DynamoDbMapper.applyUserAgent(segmentScanRequest)); - - /** - * Cache the scan result in segmentScanResponses. - * We should never try to get these scan results by calling get() on the cached future tasks. - */ - segmentScanResponses.set(currentSegment, scanResult); - - /** - * Update the state and notify any waiting thread. - */ - synchronized (segmentScanstates) { - if (null == scanResult.lastEvaluatedKey()) { - segmentScanstates.set(currentSegment, SegmentScanstate.SegmentScanCompleted); - } else { - segmentScanstates.set(currentSegment, SegmentScanstate.HasNextPage); - } - segmentScanstates.notifyAll(); - } - return scanResult; - } - - private void initSegmentScanstates() { - for (int segment = 0; segment < totalSegments; segment++) { - segmentScanFutureTasks.add(null); - segmentScanResponses.add(null); - segmentScanstates.add(SegmentScanstate.Waiting); - } - } - - /** - * Enumeration of the possible states of the scan on a segment. - */ - private enum SegmentScanstate { - /** The scan on the segment is waiting for resources to execute and has not started yet. */ - Waiting, - - /** The scan is in process, and hasn't finished yet. */ - Scanning, - - /** The scan has already failed. */ - Failed, - - /** The scan on the current page has finished, but there are more pages in the segment to be scanned. */ - HasNextPage, - - /** The scan on the whole segment has completed. */ - SegmentScanCompleted, - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/QueryResultPage.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/QueryResultPage.java deleted file mode 100644 index 569de37430a1..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/QueryResultPage.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity; - -/** - * Container for a page of query results - */ -public class QueryResultPage { - - private List results; - private Map lastEvaluatedKey; - - private Integer count; - private Integer scannedCount; - private ConsumedCapacity consumedCapacity; - - /** - * Returns all matching items for this page of query results. - */ - public List getResults() { - return results; - } - - public void setResults(List results) { - this.results = results; - } - - /** - * Returns the last evaluated key, which can be used as the - * exclusiveStartKey to fetch the next page of results. Returns null if this - * is the last page of results. - * - * @return The key-value pairs which map from the attribute name of each component - * of the primary key to its value. - */ - public Map lastEvaluatedKey() { - return lastEvaluatedKey; - } - - public void setLastEvaluatedKey(Map lastEvaluatedKey) { - this.lastEvaluatedKey = lastEvaluatedKey; - } - - /** - * The number of items in the response.

    If you used a - * QueryFilter in the request, then Count is the number of - * items returned after the filter was applied, and ScannedCount - * is the number of matching items before> the filter was applied.

    If - * you did not use a filter in the request, then Count and - * ScannedCount are the same. - * - * @return The number of items in the response.

    If you used a - * QueryFilter in the request, then Count is the number of - * items returned after the filter was applied, and ScannedCount - * is the number of matching items before> the filter was applied.

    If - * you did not use a filter in the request, then Count and - * ScannedCount are the same. - */ - public Integer getCount() { - return count; - } - - public void setCount(Integer count) { - this.count = count; - } - - /** - * The number of items evaluated, before any QueryFilter is - * applied. A high ScannedCount value with few, or no, - * Count results indicates an inefficient Query operation. - * For more information, see Count - * and ScannedCount in the Amazon DynamoDB Developer Guide. - *

    If you did not use a filter in the request, then - * ScannedCount is the same as Count. - * - * @return The number of items evaluated, before any QueryFilter is - * applied. A high ScannedCount value with few, or no, - * Count results indicates an inefficient Query operation. - * For more information, see Count - * and ScannedCount in the Amazon DynamoDB Developer Guide. - *

    If you did not use a filter in the request, then - * ScannedCount is the same as Count. - */ - public Integer scannedCount() { - return scannedCount; - } - - public void setScannedCount(Integer scannedCount) { - this.scannedCount = scannedCount; - } - - /** - * The capacity units consumed by an operation. The data returned - * includes the total provisioned throughput consumed, along with - * statistics for the table and any indexes involved in the operation. - * ConsumedCapacity is only returned if the request asked for it. - * For more information, see Provisioned - * Throughput in the Amazon DynamoDB Developer Guide. - * - * @return The capacity units consumed by an operation. The data returned - * includes the total provisioned throughput consumed, along with - * statistics for the table and any indexes involved in the operation. - * ConsumedCapacity is only returned if the request asked for it. - * For more information, see Provisioned - * Throughput in the Amazon DynamoDB Developer Guide. - */ - public ConsumedCapacity getConsumedCapacity() { - return consumedCapacity; - } - - public void setConsumedCapacity(ConsumedCapacity consumedCapacity) { - this.consumedCapacity = consumedCapacity; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/RandomUuidMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/RandomUuidMarshaller.java deleted file mode 100644 index 4a66bc8ec5aa..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/RandomUuidMarshaller.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.UUID; - -public class RandomUuidMarshaller implements DynamoDbMarshaller, DynamoDbTypeConverter { - - public static final String randomUUID = UUID.randomUUID().toString(); - - @Override - public String marshall(Object getterReturnResult) { - return randomUUID; - } - - @Override - public Object unmarshall(Class clazz, String obj) { - return null; - } - - @Override - public String convert(final Object object) { - return randomUUID; - } - - @Override - public Object unconvert(final String object) { - return null; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ReflectionUtils.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ReflectionUtils.java deleted file mode 100644 index 9727ed0a6afd..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ReflectionUtils.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import software.amazon.awssdk.utils.StringUtils; - -/** - * @deprecated Replaced by {@link StandardBeanProperties} - */ -@Deprecated -class ReflectionUtils { - - /** - * Returns the field name that corresponds to the given getter method, - * according to the Java naming convention. - * - * @param getter - * The getter method. - * @param forceCamelCase - * True if the returned field name should be in camel-case, i.e. - * the first letter is lower-cased. - */ - static String getFieldNameByGetter(Method getter, boolean forceCamelCase) { - String getterName = getter.getName(); - - String fieldNameWithUpperCamelCase = ""; - if (getterName.startsWith("get")) { - fieldNameWithUpperCamelCase = getterName.substring("get".length()); - } else if (getterName.startsWith("is")) { - fieldNameWithUpperCamelCase = getterName.substring("is".length()); - } - - if (fieldNameWithUpperCamelCase.length() == 0) { - throw new DynamoDbMappingException( - "Getter must begin with 'get' or 'is', and the field name must contain at least one character."); - } - - if (forceCamelCase) { - // Lowercase the first letter of the name - return StringUtils.lowerCase(fieldNameWithUpperCamelCase.substring(0, 1)) + fieldNameWithUpperCamelCase.substring(1); - } else { - return fieldNameWithUpperCamelCase; - } - - } - - /** - * Returns the Field object for the specified field name declared in the - * specified class. Returns null if no such field can be found. - * - * @param clazz - * The declaring class where the field will be reflected. This - * method will NOT attempt to reflect its superclass if such - * field is not found in this class. - * @param fieldName - * The case-sensitive name of the field to be searched. - */ - static Field getClassFieldByName(Class clazz, String fieldName) { - try { - return clazz.getDeclaredField(fieldName); - } catch (SecurityException e) { - throw new DynamoDbMappingException( - "Denied access to the [" + fieldName + "] field in class [" + clazz + "].", e); - } catch (NoSuchFieldException e) { - return null; - } - } - - /** - * This method searches for a specific type of annotation that is applied to - * either the specified getter method or its corresponding class field. - * Returns the annotation if it is found, else null. - */ - static T getAnnotationFromGetterOrField( - Method getter, Class annotationClass) { - // Check annotation on the getter method - T onGetter = getter.getAnnotation(annotationClass); - if (onGetter != null) { - return onGetter; - } - - // Check annotation on the corresponding field - String fieldName = getFieldNameByGetter(getter, true); - // Only consider the field declared in the same class where getter is defined. - Field field = getClassFieldByName(getter.getDeclaringClass(), fieldName); - T onField = null; - if (field != null) { - onField = field.getAnnotation(annotationClass); - } - return onField; - } - - /** - * Returns true if an annotation for the specified type is found on the - * getter method or its corresponding class field. - */ - static boolean getterOrFieldHasAnnotation( - Method getter, Class annotationClass) { - return getAnnotationFromGetterOrField(getter, annotationClass) != null; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3ClientCache.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3ClientCache.java deleted file mode 100644 index 9d2d1da59649..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3ClientCache.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import software.amazon.awssdk.auth.credentials.AwsCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.S3Configuration; - -/** - * A smart Map for {@link S3Client} objects. {@link S3ClientCache} keeps the - * clients organized by region, and if provided {@link AwsCredentials} will - * create clients on the fly. Otherwise it just return clients given to it with - * {@link #useClient(S3Client, Region)}. - */ -public class S3ClientCache { - private final ConcurrentMap clientsByRegion = new ConcurrentHashMap(); - - private final AwsCredentialsProvider awscredentialsProvider; - - @Deprecated - S3ClientCache(AwsCredentials credentials) { - this(StaticCredentialsProvider.create(credentials)); - } - - /** - * Create a client cache using the given AWSCredentialsProvider. If - * {@link #getClient(Region)} is called and a client has not been - * provided for the region, the cache will instantiate one from the - * provided {@link AwsCredentialsProvider}. - * - * @param awsCredentialsProvider - * The credentials provider to use when creating new - * {@link S3Client}. - */ - S3ClientCache(AwsCredentialsProvider awsCredentialsProvider) { - this.awscredentialsProvider = awsCredentialsProvider; - } - - - /** - * Force the client cache to provide a certain client for the region which - * that client is configured. This can be useful to provide clients with - * different {@link S3Configuration}. - * - * @param client - * An {@link S3Client} to use in the cache. Its region will - * be detected automatically. - */ - public void useClient(S3Client client, Region region) { - clientsByRegion.put(region.id(), client); - } - - /** - * Returns a client for the requested region, or throws an exception when - * unable. - * - * @param region - * The region the returned {@link S3Client} will be - * configured to use. - * @return A client for the given region from the cache, either instantiated - * automatically from the provided {@link AwsCredentials} or - * provided with {@link #useClient(S3Client, Region)}. - * @throws IllegalArgumentException - * When a region is requested that has not been provided to the - * cache with {@link #useClient(S3Client, Region)}, and the cache - * has no {@link AwsCredentials} with which a client may be - * instantiated. - */ - public S3Client getClient(Region region) { - if (region == null) { - throw new IllegalArgumentException("S3 region must be specified"); - } - return getClient(region.id()); - } - - /** - * Returns a client for the requested region, or throws an exception when - * unable. - * - * @param region - * The region the returned {@link S3Client} will be - * configured to use. - * @return A client for the given region from the cache, either instantiated - * automatically from the provided {@link AwsCredentials} or - * provided with {@link #useClient(S3Client, Region)}. - * @throws IllegalArgumentException - * When a region is requested that has not been provided to the - * cache with {@link #useClient(S3Client, Region)}, and the cache - * has no {@link AwsCredentials} with which a client may be - * instantiated. - */ - public S3Client getClient(String region) { - if (region == null) { - throw new IllegalArgumentException("S3 region must be specified"); - } - S3Client client = clientsByRegion.get(region); - return client != null ? client : cacheClient(region); - } - - /** - * Returns a new client with region configured to - * region. - * Also updates the clientsByRegion map by associating the - * new client with region. - * - * @param region - * The region the returned {@link S3Client} will be - * configured to use. - * @return A new {@link S3Client} client with region set to region. - */ - private S3Client cacheClient(String region) { - if (awscredentialsProvider == null) { - throw new IllegalArgumentException("No credentials provider found to connect to S3"); - } - S3Client client = S3Client.builder().credentialsProvider(awscredentialsProvider).region(Region.of(region)).build(); - clientsByRegion.put(region, client); - return client; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3Link.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3Link.java deleted file mode 100644 index a316bb0d44d0..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3Link.java +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.io.File; -import java.io.OutputStream; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.core.sync.RequestBody; -import software.amazon.awssdk.core.sync.ResponseTransformer; -import software.amazon.awssdk.core.util.json.JacksonUtils; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.AccessControlPolicy; -import software.amazon.awssdk.services.s3.model.GetObjectRequest; -import software.amazon.awssdk.services.s3.model.GetObjectResponse; -import software.amazon.awssdk.services.s3.model.ObjectCannedACL; -import software.amazon.awssdk.services.s3.model.PutObjectAclRequest; -import software.amazon.awssdk.services.s3.model.PutObjectRequest; -import software.amazon.awssdk.services.s3.model.PutObjectResponse; - -/** - * An S3 Link that works with {@link DynamoDbMapper}. - * An S3 link is persisted as a JSON string in DynamoDB. - * This link object can be used directly to upload/download files to S3. - * Alternatively, the underlying - * {@link S3Client} can be retrieved to - * provide full access API to S3. - *

    - * For example: - *

    - * AWSCredentialsProvider s3CredentialProvider = ...;
    - * DynamoDBMapper mapper = new DynamoDBMapper(..., s3CredentialProvider);
    - * String username = "jamestkirk";
    - *
    - * User user = new User();
    - * user.setUsername(username);
    - *
    - * // S3 region can be specified, but is optional
    - * S3Link s3link = mapper.createS3Link("my-company-user-avatars", username + ".jpg");
    - * user.setAvatar(s3link);
    - *
    - * // All meta information of the S3 resource is persisted in DynamoDB, including
    - * // region, bucket, and key
    - * mapper.save(user);
    - *
    - * // Upload file to S3 with the link saved in DynamoDB
    - * s3link.uploadFrom(new File("/path/to/all/those/user/avatars/" + username + ".jpg"));
    - * // Download file from S3 via an S3Link
    - * s3link.downloadTo(new File("/path/to/downloads/" + username + ".jpg"));
    - *
    - * // Full S3 API is available via the canonical AmazonS3Client and TransferManager API.
    - * // For example:
    - * AmazonS3Client s3 = s3link.getAmazonS3Client();
    - * TransferManager s3m = s3link.getTransferManager();
    - * // etc.
    - * 
    The User pojo class used above:
    - * @DynamoDBTable(tableName = "user-table")
    - * public class User {
    - *     private String username;
    - *     private S3Link avatar;
    - *
    - *     @DynamoDBHashKey
    - *     public String getUsername() {
    - *         return username;
    - *     }
    - *
    - *     public void setUsername(String username) {
    - *         this.username = username;
    - *     }
    - *
    - *     public S3Link getAvatar() {
    - *         return avatar;
    - *     }
    - *
    - *     public void setAvatar(S3Link avatar) {
    - *         this.avatar = avatar;
    - *     }
    - * }
    - * 
    - */ -public class S3Link { - private final S3ClientCache s3cc; - private final Id id; - - S3Link(S3ClientCache s3cc, String bucketName, String key) { - this(s3cc, new Id(bucketName, key)); - } - - S3Link(S3ClientCache s3cc, String region, String bucketName, String key) { - this(s3cc, new Id(region, bucketName, key)); - } - - private S3Link(S3ClientCache s3cc, Id id) { - this.s3cc = s3cc; - this.id = id; - - if (s3cc == null) { - throw new IllegalArgumentException("S3ClientCache must be configured for use with S3Link"); - } - if (id == null || id.bucket() == null || id.getKey() == null) { - throw new IllegalArgumentException("Bucket and key must be specified for S3Link"); - } - } - - /** - * Deserializes from a JSON string. - */ - public static S3Link fromJson(S3ClientCache s3cc, String json) { - Id id = JacksonUtils.fromJsonString(json, Id.class); - return new S3Link(s3cc, id); - } - - private static String convertRegionToString(Region region) { - return region.id(); - } - - public String getKey() { - return id.getKey(); - } - - public String bucketName() { - return id.bucket(); - } - - /** - * Returns the S3 region in {@link Region} format. - *

    - * Do not use this method if {@link S3Link} is created with a region not in {@link Region} enum. - * Use {@link #getRegion()} instead. - *

    - * - * @return S3 region. - */ - public Region s3Region() { - return Region.of(getRegion()); - } - - /** - * Returns the S3 region as string. - * - * @return region provided when creating the S3Link object. - * If no region is provided during S3Link creation, returns us-east-1. - */ - public String getRegion() { - return id.getRegionId() == null ? "us-east-1" : id.getRegionId(); - } - - /** - * Serializes into a JSON string. - * - * @return The string representation of the link to the S3 resource. - */ - public String toJson() { - return id.toJson(); - } - - public S3Client getAmazonS3Client() { - return s3cc.getClient(getRegion()); - } - - /** - * Convenience method to synchronously upload from the given file to the - * Amazon S3 object represented by this S3Link. - * - * @param source - * source file to upload from - * - * @return A {@link PutObjectResponse} object containing the information - * returned by Amazon S3 for the newly created object. - */ - public PutObjectResponse uploadFrom(final File source) { - return getAmazonS3Client().putObject(PutObjectRequest.builder() - .bucket(bucketName()) - .key(getKey()) - .build(), RequestBody.fromFile(source)); - } - - /** - * Convenience method to synchronously upload from the given buffer to the - * Amazon S3 object represented by this S3Link. - * - * @param buffer - * The buffer containing the data to upload. - * - * @return A {@link PutObjectResponse} object containing the information - * returned by Amazon S3 for the newly created object. - */ - public PutObjectResponse uploadFrom(final byte[] buffer) { - return getAmazonS3Client().putObject(PutObjectRequest.builder() - .bucket(bucketName()) - .key(getKey()) - .contentLength((long) buffer.length) - .build(), RequestBody.fromBytes(buffer)); - } - - /** - * Sets the access control list for the object represented by this S3Link. - * - * Note: Executing this method requires that the object already exists in - * Amazon S3. - * - * @param acl - * The access control list describing the new permissions for the - * object represented by this S3Link. - */ - public void setAcl(ObjectCannedACL acl) { - getAmazonS3Client().putObjectAcl(PutObjectAclRequest.builder().bucket(bucketName()).key(getKey()).acl(acl).build()); - } - - public void setAcl(AccessControlPolicy acl) { - getAmazonS3Client().putObjectAcl(PutObjectAclRequest.builder() - .accessControlPolicy(acl) - .bucket(bucketName()) - .key(getKey()) - .build()); - } - - /** - * Convenient method to synchronously download to the specified file from - * the S3 object represented by this S3Link. - * - * @param destination destination file to download to - * - * @return All S3 object metadata for the specified object. - * Returns null if constraints were specified but not met. - */ - public GetObjectResponse downloadTo(final File destination) { - return getAmazonS3Client().getObject(GetObjectRequest.builder() - .bucket(bucketName()) - .key(getKey()) - .build(), - ResponseTransformer.toFile(destination.toPath())); - } - - /** - * Downloads the data from the object represented by this S3Link to the - * specified output stream. - * - * @param output - * The output stream to write the object's data to. - * - * @return The object's metadata. - */ - public GetObjectResponse downloadTo(final OutputStream output) { - return getAmazonS3Client().getObject(GetObjectRequest.builder() - .bucket(bucketName()) - .key(getKey()) - .build(), - ResponseTransformer.toOutputStream(output)); - } - - /** - * JSON wrapper of an {@link S3Link} identifier, - * which consists of the S3 region id, bucket name and key. - * Sample JSON serialized form: - *
    -     * {"s3":{"bucket":"mybucket","key":"mykey","region":"us-west-2"}}
    -     * {"s3":{"bucket":"mybucket","key":"mykey","region":null}}
    -     * 
    - * Note for S3 a null region means US standard. - *

    - * @see Region - */ - static class Id { - @JsonProperty("s3") - private S3 s3; - - Id() { - } // used by Jackson to unmarshall - - Id(String bucketName, String key) { - this.s3 = new S3(bucketName, key); - } - - Id(String region, String bucketName, String key) { - this.s3 = new S3(region, bucketName, key); - } - - Id(S3 s3) { - this.s3 = s3; - } - - @JsonProperty("s3") - public S3 s3() { - return s3; - } - - @JsonIgnore - public String getRegionId() { - return s3.getRegionId(); - } - - @JsonIgnore - public String bucket() { - return s3.bucket(); - } - - @JsonIgnore - public String getKey() { - return s3.getKey(); - } - - String toJson() { - return JacksonUtils.toJsonString(this); - } - } - - /** - * Internal class for JSON serialization purposes. - *

    - * @see Id - */ - private static class S3 { - - /** - * The name of the S3 bucket containing the object to retrieve. - */ - @JsonProperty("bucket") - private String bucket; - - /** - * The key under which the desired S3 object is stored. - */ - @JsonProperty("key") - private String key; - - /** - * The region id of {@link Region} where the S3 object is stored. - */ - @JsonProperty("region") - private String regionId; - - @SuppressWarnings("unused") - S3() { - } // used by Jackson to unmarshall - - /** - * Constructs a new {@link S3} with all the required parameters. - * - * @param bucket - * The name of the bucket containing the desired object. - * @param key - * The key in the specified bucket under which the object is - * stored. - */ - S3(String bucket, String key) { - this(null, bucket, key); - } - - /** - * Constructs a new {@link S3} with all the required parameters. - * - * @param region - * The region where the S3 object is stored. - * @param bucket - * The name of the bucket containing the desired object. - * @param key - * The key in the specified bucket under which the object is - * stored. - */ - S3(String region, String bucket, String key) { - this.regionId = region; - this.bucket = bucket; - this.key = key; - } - - /** - * Gets the name of the bucket containing the object to be downloaded. - * - * @return The name of the bucket containing the object to be downloaded. - */ - @JsonProperty("bucket") - public String bucket() { - return bucket; - } - - /** - * Gets the key under which the object to be downloaded is stored. - * - * @return The key under which the object to be downloaded is stored. - */ - @JsonProperty("key") - public String getKey() { - return key; - } - - @JsonProperty("region") - public String getRegionId() { - return regionId; - } - } - - /** - * {@link S3Link} factory. - */ - public static final class Factory implements DynamoDbTypeConverter { - static final Factory DEFAULT = new Factory((S3ClientCache) null); - private final S3ClientCache s3cc; - - public Factory(final S3ClientCache s3cc) { - this.s3cc = s3cc; - } - - public static Factory of(final AwsCredentialsProvider provider) { - return provider == null ? DEFAULT : new Factory(new S3ClientCache(provider)); - } - - public S3Link createS3Link(Region s3region, String bucketName, String key) { - return createS3Link(convertRegionToString(s3region), bucketName, key); - } - - public S3Link createS3Link(String s3region, String bucketName, String key) { - if (s3ClientCache() == null) { - throw new IllegalStateException("Mapper must be constructed with S3 AWS Credentials to create S3Link"); - } - return new S3Link(s3ClientCache(), s3region, bucketName, key); - } - - public S3ClientCache s3ClientCache() { - return this.s3cc; - } - - @Override - public String convert(final S3Link o) { - return o.bucketName() == null || o.getKey() == null ? null : o.toJson(); - } - - @Override - public S3Link unconvert(final String o) { - return S3Link.fromJson(s3ClientCache(), o); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3LinkIdTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3LinkIdTest.java deleted file mode 100644 index fd8b4d42ddc3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3LinkIdTest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; -import software.amazon.awssdk.core.util.json.JacksonUtils; - -public class S3LinkIdTest { - - @Test - public void testToFromJson() { - String region = "ap-northeast-1"; - S3Link.Id id = new S3Link.Id(region, "bucket", "key"); - String json = id.toJson(); - S3Link.Id twin = JacksonUtils.fromJsonString(json, S3Link.Id.class); - assertEquals("bucket", twin.bucket()); - assertEquals("key", twin.getKey()); - assertEquals(region, twin.getRegionId()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3LinkTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3LinkTest.java deleted file mode 100644 index 8b5789868846..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/S3LinkTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentials; -import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; - -@Ignore // FIXME: Setup fails with "region cannot be null" -public class S3LinkTest { - private DynamoDbMapper mapper; - - @Before - public void setUp() { - AwsCredentials credentials = AwsBasicCredentials.create("mock", "mock"); - DynamoDbClient db = DynamoDbClient.builder() - .credentialsProvider(StaticCredentialsProvider.create(credentials)) - .region(Region.US_WEST_2) - .build(); - mapper = new DynamoDbMapper(db, StaticCredentialsProvider.create(credentials)); - } - - @Test(expected = IllegalArgumentException.class) - public void nullKey() { - mapper.createS3Link("bucket", null); - } - - @Test(expected = IllegalArgumentException.class) - public void nullBucketName() { - mapper.createS3Link(null, "key"); - } - - @Test - public void testToJson() { - S3Link testLink = mapper.createS3Link("bucket", "key"); - String json = testLink.toJson(); - - assertEquals(json, - "{\"s3\":{\"bucket\":\"bucket\",\"key\":\"key\",\"region\":\"us-east-1\"}}", - json); - testLink = mapper.createS3Link("bucket", "testKey"); - json = testLink.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"bucket\",\"key\":\"testKey\",\"region\":\"us-east-1\"}}", - json); - - testLink = mapper.createS3Link(Region.AP_SOUTHEAST_2, "bucket", "testKey"); - json = testLink.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"bucket\",\"key\":\"testKey\",\"region\":\"ap-southeast-2\"}}", - json); - - testLink = mapper.createS3Link(Region.AP_SOUTHEAST_2, "test-bucket", "testKey"); - json = testLink.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"test-bucket\",\"key\":\"testKey\",\"region\":\"ap-southeast-2\"}}", - json); - - testLink = mapper.createS3Link(Region.AP_SOUTHEAST_2, "test-bucket", "test/key/with/slashes"); - json = testLink.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"test-bucket\",\"key\":\"test/key/with/slashes\",\"region\":\"ap-southeast-2\"}}", - json); - - testLink = mapper.createS3Link("test-bucket", "test/key/with/slashes"); - json = testLink.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"test-bucket\",\"key\":\"test/key/with/slashes\",\"region\":\"us-east-1\"}}", - json); - testLink = mapper.createS3Link(Region.AP_SOUTHEAST_2, "test-bucket", "test/key/with/slashes"); - json = testLink.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"test-bucket\",\"key\":\"test/key/with/slashes\",\"region\":\"ap-southeast-2\"}}", - json); - } - - @Test - public void testFromJson() { - String json = "{\"s3\":{\"region\":\"ap-southeast-2\",\"bucket\":\"test-bucket\",\"key\":\"testKey\"}}"; - S3Link s3link = S3Link.fromJson(mapper.s3ClientCache(), json); - assertEquals("test-bucket", s3link.bucketName()); - assertEquals("ap-southeast-2", s3link.getRegion()); - assertEquals("testKey", s3link.getKey()); - } - - @Test - public void testDefaultRegion() { - S3Link testLink1 = mapper.createS3Link("bucket", "key"); - String json = testLink1.toJson(); - // Default to US_STANDARD if not specified - assertEquals(json, - "{\"s3\":{\"bucket\":\"bucket\",\"key\":\"key\",\"region\":\"us-east-1\"}}", - json); - // Default region changed to GovCloud - testLink1 = mapper.createS3Link(Region.US_GOV_WEST_1, "bucket", "key"); - json = testLink1.toJson(); - assertEquals(json, - "{\"s3\":{\"bucket\":\"bucket\",\"key\":\"key\",\"region\":\"us-gov-west-1\"}}", - json); - } - - @Test - public void testGetRegion_ReturnsUsEast1_Whens3LinkCreated_WithNullRegion() { - S3Link s3Link = mapper.createS3Link("bucket", "key"); - - assertEquals("us-east-1", s3Link.s3Region().id()); - assertEquals("us-east-1", s3Link.getRegion()); - } - - @Test - public void testGetRegion_ReturnsUsEast1_WhenS3LinkCreated_WithUsStandardRegion() { - S3Link s3Link = mapper.createS3Link(Region.US_EAST_1, "bucket", "key"); - - assertEquals("us-east-1", s3Link.s3Region().id()); - assertEquals("us-east-1", s3Link.getRegion()); - } - - @Test - public void testGetRegion_ReturnsUsEast1_Whens3LinkCreated_WithUsEast1Region() { - S3Link s3Link = mapper.createS3Link("us-east-1", "bucket", "key"); - - assertEquals("us-east-1", s3Link.s3Region().id()); - assertEquals("us-east-1", s3Link.getRegion()); - } - - @Test - public void testGetRegion_WithNonUsStandardRegion() { - S3Link s3Link = mapper.createS3Link(Region.EU_WEST_2, "bucket", "key"); - - assertEquals(Region.EU_WEST_2, s3Link.s3Region()); - assertEquals(Region.EU_WEST_2.id(), s3Link.getRegion()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ScanResultPage.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ScanResultPage.java deleted file mode 100644 index d3a8b28f722b..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/ScanResultPage.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.List; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity; - -/** - * Container for a page of scan results. - */ -public class ScanResultPage { - - private List results; - private Map lastEvaluatedKey; - - private Integer count; - private Integer scannedCount; - private ConsumedCapacity consumedCapacity; - - /** - * Returns all matching items for this page of scan results, which may be - * empty. - */ - public List getResults() { - return results; - } - - public void setResults(List results) { - this.results = results; - } - - /** - * Returns the last evaluated key, which can be used as the - * exclusiveStartKey to fetch the next page of results. Returns null if this - * is the last page of results. - * - * @return The key-value pairs which map from the attribute name of each component - * of the primary key to its value. - */ - public Map lastEvaluatedKey() { - return lastEvaluatedKey; - } - - public void setLastEvaluatedKey(Map lastEvaluatedKey) { - this.lastEvaluatedKey = lastEvaluatedKey; - } - - /** - * The number of items in the response.

    If you set ScanFilter - * in the request, then Count is the number of items returned - * after the filter was applied, and ScannedCount is the number of - * matching items before the filter was applied.

    If you did not use a - * filter in the request, then Count is the same as - * ScannedCount. - * - * @return The number of items in the response.

    If you set ScanFilter - * in the request, then Count is the number of items returned - * after the filter was applied, and ScannedCount is the number of - * matching items before the filter was applied.

    If you did not use a - * filter in the request, then Count is the same as - * ScannedCount. - */ - public Integer getCount() { - return count; - } - - public void setCount(Integer count) { - this.count = count; - } - - /** - * The number of items evaluated, before any ScanFilter is - * applied. A high ScannedCount value with few, or no, - * Count results indicates an inefficient Scan operation. - * For more information, see Count - * and ScannedCount in the Amazon DynamoDB Developer Guide. - *

    If you did not use a filter in the request, then - * ScannedCount is the same as Count. - * - * @return The number of items evaluated, before any ScanFilter is - * applied. A high ScannedCount value with few, or no, - * Count results indicates an inefficient Scan operation. - * For more information, see Count - * and ScannedCount in the Amazon DynamoDB Developer Guide. - *

    If you did not use a filter in the request, then - * ScannedCount is the same as Count. - */ - public Integer scannedCount() { - return scannedCount; - } - - public void setScannedCount(Integer scannedCount) { - this.scannedCount = scannedCount; - } - - /** - * The capacity units consumed by an operation. The data returned - * includes the total provisioned throughput consumed, along with - * statistics for the table and any indexes involved in the operation. - * ConsumedCapacity is only returned if the request asked for it. - * For more information, see Provisioned - * Throughput in the Amazon DynamoDB Developer Guide. - * - * @return The capacity units consumed by an operation. The data returned - * includes the total provisioned throughput consumed, along with - * statistics for the table and any indexes involved in the operation. - * ConsumedCapacity is only returned if the request asked for it. - * For more information, see Provisioned - * Throughput in the Amazon DynamoDB Developer Guide. - */ - public ConsumedCapacity getConsumedCapacity() { - return consumedCapacity; - } - - public void setConsumedCapacity(ConsumedCapacity consumedCapacity) { - this.consumedCapacity = consumedCapacity; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardAnnotationMaps.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardAnnotationMaps.java deleted file mode 100644 index 392753341a10..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardAnnotationMaps.java +++ /dev/null @@ -1,459 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGenerateStrategy.CREATE; -import static software.amazon.awssdk.services.dynamodb.model.KeyType.HASH; -import static software.amazon.awssdk.services.dynamodb.model.KeyType.RANGE; - -import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.Collections; -import java.util.EnumMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; -import software.amazon.awssdk.services.dynamodb.model.KeyType; - -/** - * Map of DynamoDB annotations. - */ -@SdkInternalApi -final class StandardAnnotationMaps { - - /** - * Gets all the DynamoDB annotations for a given class. - */ - static TableMap of(Class clazz) { - final TableMap annotations = new TableMap(clazz); - annotations.putAll(clazz); - return annotations; - } - - /** - * Gets all the DynamoDB annotations; method annotations override field - * level annotations which override class/type level annotations. - */ - static FieldMap of(Method getter, String defaultName) { - final Class targetType = (Class) getter.getReturnType(); - final String fieldName = StandardBeanProperties.fieldNameOf(getter); - - Field declaredField = null; - try { - declaredField = getter.getDeclaringClass().getDeclaredField(fieldName); - } catch (final NoSuchFieldException no) { - // Ignored or expected. - } catch (final SecurityException e) { - throw new DynamoDbMappingException("no access to field for " + getter, e); - } - - if (defaultName == null) { - defaultName = fieldName; - } - - final FieldMap annotations = new FieldMap(targetType, defaultName); - annotations.putAll(targetType); - annotations.putAll(declaredField); - annotations.putAll(getter); - return annotations; - } - - /** - * Creates a new instance of the clazz with the target type and annotation - * as parameters if available. - */ - private static T overrideOf(Class clazz, Class targetType, Annotation annotation) { - try { - if (annotation != null) { - try { - Constructor c = clazz.getDeclaredConstructor(Class.class, annotation.annotationType()); - return c.newInstance(targetType, annotation); - } catch (final NoSuchMethodException no) { - // Ignored or expected. - } - } - try { - return clazz.getDeclaredConstructor(Class.class).newInstance(targetType); - } catch (final NoSuchMethodException no) { - // Ignored or expected. - } - return clazz.newInstance(); - } catch (final IllegalAccessException | InstantiationException | InvocationTargetException | RuntimeException e) { - throw new DynamoDbMappingException("could not instantiate " + clazz, e); - } - } - - /** - * Common type-conversions properties. - */ - private abstract static class AbstractAnnotationMap { - private final Annotations map = new Annotations(); - - /** - * Gets the actual annotation by type; if the type is not directly - * mapped then the meta-annotation is returned. - */ - final A actualOf(final Class annotationType) { - final Annotation annotation = this.map.get(annotationType); - if (annotation == null || annotation.annotationType() == annotationType) { - return (A) annotation; - } else if (annotation.annotationType().isAnnotationPresent(annotationType)) { - return annotation.annotationType().getAnnotation(annotationType); - } - throw new DynamoDbMappingException( - "could not resolve annotation by type" + - "; @" + annotationType.getSimpleName() + " not present on " + annotation - ); - } - - /** - * Puts all DynamoDB annotations into the map. - */ - final void putAll(AnnotatedElement annotated) { - if (annotated != null) { - this.map.putAll(new Annotations().putAll(annotated.getAnnotations())); - } - } - } - - /** - * Common type-conversions properties. - */ - abstract static class TypedMap extends AbstractAnnotationMap { - private final Class targetType; - - private TypedMap(final Class targetType) { - this.targetType = targetType; - } - - /** - * Gets the target type. - */ - final Class targetType() { - return this.targetType; - } - - /** - * Gets the attribute type from the {@link DynamoDbTyped} annotation - * if present. - */ - public DynamoDbAttributeType attributeType() { - final DynamoDbTyped annotation = actualOf(DynamoDbTyped.class); - if (annotation != null) { - return annotation.value(); - } - return null; - } - - /** - * Creates a new type-converter form the {@link DynamoDbTypeConverted} - * annotation if present. - */ - public DynamoDbTypeConverter typeConverter() { - Annotation annotation = super.map.get(DynamoDbTypeConverted.class); - if (annotation != null) { - final DynamoDbTypeConverted converted = actualOf(DynamoDbTypeConverted.class); - annotation = (converted == annotation ? null : annotation); - return overrideOf(converted.converter(), targetType, annotation); - } - return null; - } - - /** - * Creates a new auto-generator from the {@link DynamoDbAutoGenerated} - * annotation if present. - */ - public DynamoDbAutoGenerator autoGenerator() { - Annotation annotation = super.map.get(DynamoDbAutoGenerated.class); - if (annotation != null) { - final DynamoDbAutoGenerated generated = actualOf(DynamoDbAutoGenerated.class); - annotation = (generated == annotation ? null : annotation); - DynamoDbAutoGenerator generator = overrideOf(generated.generator(), targetType, annotation); - if (generator.getGenerateStrategy() == CREATE && targetType.isPrimitive()) { - throw new DynamoDbMappingException( - "type [" + targetType + "] is not supported for auto-generation" + - "; primitives are not allowed when auto-generate strategy is CREATE" - ); - } - return generator; - } - return null; - } - - /** - * Maps the attributes from the {@link DynamoDbFlattened} annotation. - */ - public Map attributes() { - final Map attributes = new LinkedHashMap(); - for (final DynamoDbAttribute a : actualOf(DynamoDbFlattened.class).attributes()) { - if (a.mappedBy().isEmpty() || a.attributeName().isEmpty()) { - throw new DynamoDbMappingException("@DynamoDBFlattened must specify mappedBy and attributeName"); - } else if (attributes.put(a.mappedBy(), a.attributeName()) != null) { - throw new DynamoDbMappingException("@DynamoDBFlattened must not duplicate mappedBy=" + a.mappedBy()); - } - } - if (attributes.isEmpty()) { - throw new DynamoDbMappingException("@DynamoDBFlattened must specify one or more attributes"); - } - return attributes; - } - - /** - * Returns true if the {@link DynamoDbFlattened} annotation is present. - */ - public boolean flattened() { - return actualOf(DynamoDbFlattened.class) != null; - } - } - - /** - * {@link DynamoDbMapperTableModel} annotations. - */ - static final class TableMap extends TypedMap implements DynamoDbMapperTableModel.Properties { - private TableMap(final Class targetType) { - super(targetType); - } - - /** - * {@inheritDoc} - */ - @Override - public DynamoDbAttributeType attributeType() { - DynamoDbAttributeType attributeType = super.attributeType(); - if (attributeType == null && actualOf(DynamoDbTable.class) != null) { - attributeType = DynamoDbAttributeType.M; - } - return attributeType; - } - - /** - * {@inheritDoc} - */ - @Override - public String tableName() { - final DynamoDbTable annotation = actualOf(DynamoDbTable.class); - if (annotation != null && !annotation.tableName().isEmpty()) { - return annotation.tableName(); - } - return null; - } - } - - /** - * {@link DynamoDbMapperFieldModel} annotations. - */ - static final class FieldMap extends TypedMap implements DynamoDbMapperFieldModel.Properties { - private final String defaultName; - - private FieldMap(Class targetType, String defaultName) { - super(targetType); - this.defaultName = defaultName; - } - - /** - * Returns true if the {@link DynamoDbIgnore} annotation is present. - */ - public boolean ignored() { - return actualOf(DynamoDbIgnore.class) != null; - } - - /** - * {@inheritDoc} - */ - @Override - public DynamoDbAttributeType attributeType() { - final DynamoDbScalarAttribute annotation = actualOf(DynamoDbScalarAttribute.class); - if (annotation != null) { - if (Set.class.isAssignableFrom(targetType())) { - return DynamoDbAttributeType.valueOf(annotation.type().name() + "S"); - } else { - return DynamoDbAttributeType.valueOf(annotation.type().name()); - } - } - return super.attributeType(); - } - - /** - * {@inheritDoc} - */ - @Override - public String attributeName() { - final DynamoDbHashKey hashKey = actualOf(DynamoDbHashKey.class); - if (hashKey != null && !hashKey.attributeName().isEmpty()) { - return hashKey.attributeName(); - } - final DynamoDbIndexHashKey indexHashKey = actualOf(DynamoDbIndexHashKey.class); - if (indexHashKey != null && !indexHashKey.attributeName().isEmpty()) { - return indexHashKey.attributeName(); - } - final DynamoDbRangeKey rangeKey = actualOf(DynamoDbRangeKey.class); - if (rangeKey != null && !rangeKey.attributeName().isEmpty()) { - return rangeKey.attributeName(); - } - final DynamoDbIndexRangeKey indexRangeKey = actualOf(DynamoDbIndexRangeKey.class); - if (indexRangeKey != null && !indexRangeKey.attributeName().isEmpty()) { - return indexRangeKey.attributeName(); - } - final DynamoDbAttribute attribute = actualOf(DynamoDbAttribute.class); - if (attribute != null && !attribute.attributeName().isEmpty()) { - return attribute.attributeName(); - } - final DynamoDbVersionAttribute versionAttribute = actualOf(DynamoDbVersionAttribute.class); - if (versionAttribute != null && !versionAttribute.attributeName().isEmpty()) { - return versionAttribute.attributeName(); - } - final DynamoDbScalarAttribute scalarAttribute = actualOf(DynamoDbScalarAttribute.class); - if (scalarAttribute != null && !scalarAttribute.attributeName().isEmpty()) { - return scalarAttribute.attributeName(); - } - final DynamoDbNamed annotation = actualOf(DynamoDbNamed.class); - if (annotation != null && !annotation.value().isEmpty()) { - return annotation.value(); - } - return this.defaultName; - } - - /** - * {@inheritDoc} - */ - @Override - public KeyType keyType() { - final DynamoDbKeyed annotation = actualOf(DynamoDbKeyed.class); - if (annotation != null) { - return annotation.value(); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean versioned() { - return actualOf(DynamoDbVersioned.class) != null; - } - - /** - * {@inheritDoc} - */ - @Override - public Map> globalSecondaryIndexNames() { - final Map> gsis = new EnumMap>(KeyType.class); - final DynamoDbIndexHashKey indexHashKey = actualOf(DynamoDbIndexHashKey.class); - if (indexHashKey != null) { - if (!indexHashKey.globalSecondaryIndexName().isEmpty()) { - if (indexHashKey.globalSecondaryIndexNames().length > 0) { - throw new DynamoDbMappingException("@DynamoDBIndexHashKey must not specify both HASH GSI name/names"); - } - gsis.put(HASH, Collections.singletonList(indexHashKey.globalSecondaryIndexName())); - } else if (indexHashKey.globalSecondaryIndexNames().length > 0) { - gsis.put(HASH, Collections.unmodifiableList(Arrays.asList(indexHashKey.globalSecondaryIndexNames()))); - } else { - throw new DynamoDbMappingException("@DynamoDBIndexHashKey must specify one of HASH GSI name/names"); - } - } - final DynamoDbIndexRangeKey indexRangeKey = actualOf(DynamoDbIndexRangeKey.class); - if (indexRangeKey != null) { - if (!indexRangeKey.globalSecondaryIndexName().isEmpty()) { - if (indexRangeKey.globalSecondaryIndexNames().length > 0) { - throw new DynamoDbMappingException("@DynamoDBIndexRangeKey must not specify both RANGE GSI name/names"); - } - gsis.put(RANGE, Collections.singletonList(indexRangeKey.globalSecondaryIndexName())); - } else if (indexRangeKey.globalSecondaryIndexNames().length > 0) { - gsis.put(RANGE, Collections.unmodifiableList(Arrays.asList(indexRangeKey.globalSecondaryIndexNames()))); - } else if (localSecondaryIndexNames().isEmpty()) { - throw new DynamoDbMappingException("@DynamoDBIndexRangeKey must specify RANGE GSI and/or LSI name/names"); - } - } - if (!gsis.isEmpty()) { - return Collections.unmodifiableMap(gsis); - } - return Collections.>emptyMap(); - } - - /** - * {@inheritDoc} - */ - @Override - public List localSecondaryIndexNames() { - final DynamoDbIndexRangeKey annotation = actualOf(DynamoDbIndexRangeKey.class); - if (annotation != null) { - if (!annotation.localSecondaryIndexName().isEmpty()) { - if (annotation.localSecondaryIndexNames().length > 0) { - throw new DynamoDbMappingException("@DynamoDBIndexRangeKey must not specify both LSI name/names"); - } - return Collections.singletonList(annotation.localSecondaryIndexName()); - } else if (annotation.localSecondaryIndexNames().length > 0) { - return Collections.unmodifiableList(Arrays.asList(annotation.localSecondaryIndexNames())); - } - } - return Collections.emptyList(); - } - } - - /** - * A map of annotation type to annotation. It will map any first level - * custom annotations to any DynamoDB annotation types that are present. - * It will support up to two levels of compounded DynamoDB annotations. - */ - private static final class Annotations extends LinkedHashMap, Annotation> { - private static final long serialVersionUID = -1L; - - /** - * Puts the annotation if it's DynamoDB; ensures there are no conflicts. - */ - public boolean putIfAnnotated(Class annotationType, Annotation annotation) { - if (!annotationType.isAnnotationPresent(DynamoDb.class)) { - return false; - } else { - annotation = put(annotationType, annotation); - if (annotation == null) { - return true; - } - } - throw new DynamoDbMappingException( - "conflicting annotations " + annotation + " and " + get(annotationType) + - "; allowed only one of @" + annotationType.getSimpleName() - ); - } - - /** - * Puts all DynamoDB annotations and meta-annotations in the map. - */ - public Annotations putAll(Annotation... annotations) { - for (final Annotation a1 : annotations) { - putIfAnnotated(a1.annotationType(), a1); - for (final Annotation a2 : a1.annotationType().getAnnotations()) { - if (putIfAnnotated(a2.annotationType(), a1)) { - for (final Annotation a3 : a2.annotationType().getAnnotations()) { - putIfAnnotated(a3.annotationType(), a2); - } - } - } - } - return this; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardBeanProperties.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardBeanProperties.java deleted file mode 100644 index 2283cddfddcf..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardBeanProperties.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.Reflect; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardAnnotationMaps.FieldMap; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardAnnotationMaps.TableMap; -import software.amazon.awssdk.utils.StringUtils; - -/** - * Reflection assistant for {@link DynamoDbMapper} - */ -@SdkInternalApi -final class StandardBeanProperties { - - /** - * Returns the bean mappings for a given class (caches the results). - */ - @SuppressWarnings("unchecked") - static Beans of(Class clazz) { - return ((CachedBeans) CachedBeans.CACHE).beans(clazz); - } - - /** - * Gets the field name given the getter method. - */ - static String fieldNameOf(Method getter) { - final String name = getter.getName().replaceFirst("^(get|is)", ""); - return StringUtils.lowerCase(name.substring(0, 1)) + name.substring(1); - } - - /** - * Cache of {@link Beans} by class type. - */ - private static final class CachedBeans { - private static final CachedBeans CACHE = new CachedBeans(); - private final ConcurrentMap, Beans> cache = new ConcurrentHashMap, Beans>(); - - private Beans beans(Class clazz) { - if (!cache.containsKey(clazz)) { - final TableMap annotations = StandardAnnotationMaps.of(clazz); - final BeanMap map = new BeanMap(clazz, false); - cache.putIfAbsent(clazz, new Beans(annotations, map)); - } - return cache.get(clazz); - } - } - - /** - * Cache of {@link Bean} mappings by class type. - */ - static final class Beans { - private final DynamoDbMapperTableModel.Properties properties; - private final Map> map; - - private Beans(TableMap annotations, Map> map) { - this.properties = new DynamoDbMapperTableModel.Properties.Immutable(annotations); - this.map = Collections.unmodifiableMap(map); - } - - DynamoDbMapperTableModel.Properties properties() { - return this.properties; - } - - Map> map() { - return this.map; - } - } - - /** - * Holds the reflection bean properties for a given property. - */ - static final class Bean { - private final DynamoDbMapperFieldModel.Properties properties; - private final ConvertibleType type; - private final Reflect reflect; - - private Bean(FieldMap annotations, Reflect reflect, Method getter) { - this.properties = new DynamoDbMapperFieldModel.Properties.Immutable(annotations); - this.type = ConvertibleType.of(getter, annotations); - this.reflect = reflect; - } - - DynamoDbMapperFieldModel.Properties properties() { - return this.properties; - } - - ConvertibleType type() { - return this.type; - } - - Reflect reflect() { - return this.reflect; - } - } - - /** - * Get/set reflection operations. - */ - static final class MethodReflect implements Reflect { - private final Method getter; - private final Method setter; - - private MethodReflect(Method getter) { - this.setter = setterOf(getter); - this.getter = getter; - } - - static Method setterOf(Method getter) { - try { - final String name = "set" + getter.getName().replaceFirst("^(get|is)", ""); - return getter.getDeclaringClass().getMethod(name, getter.getReturnType()); - } catch (NoSuchMethodException | RuntimeException no) { - // Ignored or expected. - } - return null; - } - - @Override - public V get(T object) { - try { - return (V) getter.invoke(object); - } catch (final Exception e) { - throw new DynamoDbMappingException("could not invoke " + getter + " on " + object.getClass(), e); - } - } - - @Override - public void set(T object, V value) { - try { - setter.invoke(object, value); - } catch (final Exception e) { - throw new DynamoDbMappingException("could not invoke " + setter + " on " + object.getClass() + - " with value " + value + " of type " + - (value == null ? null : value.getClass()), e); - } - } - } - - /** - * Get/set reflection operations with a declaring property. - */ - static final class DeclaringReflect implements Reflect { - private final Reflect reflect; - private final Reflect declaring; - private final Class targetType; - - private DeclaringReflect(Method getter, Reflect declaring, Class targetType) { - this.reflect = new MethodReflect(getter); - this.declaring = declaring; - this.targetType = targetType; - } - - static T newInstance(Class targetType) { - try { - return targetType.newInstance(); - } catch (final Exception e) { - throw new DynamoDbMappingException("could not instantiate " + targetType, e); - } - } - - @Override - public V get(T object) { - final T declaringObject = declaring.get(object); - if (declaringObject == null) { - return null; - } - return reflect.get(declaringObject); - } - - @Override - public void set(T object, V value) { - T declaringObject = declaring.get(object); - if (declaringObject == null) { - declaringObject = newInstance(targetType); - declaring.set(object, declaringObject); - } - reflect.set(declaringObject, value); - } - } - - /** - * {@link Map} of {@link Bean} - */ - static final class BeanMap extends LinkedHashMap> { - public static final long serialVersionUID = 1L; - - private final Class clazz; - - BeanMap(Class clazz, boolean inherited) { - this.clazz = clazz; - putAll(clazz, inherited); - } - - private void putAll(Class clazz, boolean inherited) { - for (final Method method : clazz.getMethods()) { - if (canMap(method, inherited)) { - final FieldMap annotations = StandardAnnotationMaps.of(method, null); - if (!annotations.ignored()) { - final Reflect reflect = new MethodReflect(method); - putOrFlatten(annotations, reflect, method); - } - } - } - } - - private void putOrFlatten(FieldMap annotations, Reflect reflect, Method getter) { - if (annotations.flattened()) { - flatten((Class) annotations.targetType(), annotations.attributes(), (Reflect) reflect); - } else { - final Bean bean = new Bean(annotations, reflect, getter); - if (put(bean.properties().attributeName(), bean) != null) { - throw new DynamoDbMappingException("duplicate attribute name"); - } - } - } - - private void flatten(Class targetType, Map attributes, Reflect declaring) { - for (final Method method : targetType.getMethods()) { - if (canMap(method, true)) { - String name = fieldNameOf(method); - name = attributes.remove(name); - if (name == null) { - continue; - } - final FieldMap annotations = StandardAnnotationMaps.of(method, name); - if (!annotations.ignored()) { - final Reflect reflect = new DeclaringReflect(method, declaring, targetType); - putOrFlatten(annotations, reflect, method); - } - } - } - if (!attributes.isEmpty()) { //<- this should be empty by now - throw new DynamoDbMappingException("contains unknown flattened attribute(s): " + attributes); - } - } - - private boolean canMap(Method method, boolean inherited) { - if (method.getName().matches("^(get|is).+") == false) { - return false; - } else if (method.getParameterTypes().length != 0) { - return false; - } else if (method.isBridge() || method.isSynthetic()) { - return false; - } else if (method.getDeclaringClass() == Object.class) { - return false; - } else if (!inherited && method.getDeclaringClass() != this.clazz && - StandardAnnotationMaps.of(method.getDeclaringClass()).attributeType() == null) { - return false; - } else { - return true; - } - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactories.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactories.java deleted file mode 100644 index bc5d9e8d08a1..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactories.java +++ /dev/null @@ -1,801 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static java.util.stream.Collectors.toList; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Scalar.BOOLEAN; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Scalar.DEFAULT; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Scalar.STRING; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Vector.LIST; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Vector.MAP; -import static software.amazon.awssdk.services.dynamodb.datamodeling.StandardTypeConverters.Vector.SET; -import static software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType.B; -import static software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType.N; -import static software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType.S; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.ImmutableObjectUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.Reflect; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperModelFactory.TableFactory; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConverter.AbstractConverter; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTypeConverter.DelegateConverter; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardBeanProperties.Bean; -import software.amazon.awssdk.services.dynamodb.datamodeling.StandardBeanProperties.Beans; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * Pre-defined strategies for mapping between Java types and DynamoDB types. - */ -@SdkInternalApi -final class StandardModelFactories { - - private static final Logger log = LoggerFactory.getLogger(StandardModelFactories.class); - - /** - * Creates the standard {@link DynamoDbMapperModelFactory} factory. - */ - static DynamoDbMapperModelFactory of(S3Link.Factory s3Links) { - return new StandardModelFactory(s3Links); - } - - /** - * Creates a new set of conversion rules based on the configuration. - */ - private static RuleFactory rulesOf(DynamoDbMapperConfig config, S3Link.Factory s3Links, - DynamoDbMapperModelFactory models) { - final boolean ver1 = (config.getConversionSchema() == ConversionSchemas.V1); - final boolean ver2 = (config.getConversionSchema() == ConversionSchemas.V2); - final boolean v2Compatible = (config.getConversionSchema() == ConversionSchemas.V2_COMPATIBLE); - - final DynamoDbTypeConverterFactory.Builder scalars = config.getTypeConverterFactory().override(); - scalars.with(String.class, S3Link.class, s3Links); - - final Rules factory = new Rules(scalars.build()); - factory.add(factory.new NativeType(!ver1)); - factory.add(factory.new V2CompatibleBool(v2Compatible)); - factory.add(factory.new NativeBool(ver2)); - factory.add(factory.new StringScalar(true)); - factory.add(factory.new NumberScalar(true)); - factory.add(factory.new BinaryScalar(true)); - factory.add(factory.new NativeBoolSet(ver2)); - factory.add(factory.new StringScalarSet(true)); - factory.add(factory.new NumberScalarSet(true)); - factory.add(factory.new BinaryScalarSet(true)); - factory.add(factory.new ObjectSet(ver2)); - factory.add(factory.new ObjectStringSet(!ver2)); - factory.add(factory.new ObjectList(!ver1)); - factory.add(factory.new ObjectMap(!ver1)); - factory.add(factory.new ObjectDocumentMap(!ver1, models, config)); - return factory; - } - - /** - * Attribute value conversion. - */ - interface Rule { - boolean isAssignableFrom(ConvertibleType type); - - DynamoDbTypeConverter newConverter(ConvertibleType type); - - DynamoDbAttributeType getAttributeType(); - } - - /** - * Attribute value conversion factory. - */ - interface RuleFactory { - Rule getRule(ConvertibleType type); - } - - /** - * {@link TableFactory} mapped by {@link ConversionSchema}. - */ - private static final class StandardModelFactory implements DynamoDbMapperModelFactory { - private final ConcurrentMap cache; - private final S3Link.Factory s3Links; - - private StandardModelFactory(S3Link.Factory s3Links) { - this.cache = new ConcurrentHashMap(); - this.s3Links = s3Links; - } - - @Override - public TableFactory getTableFactory(DynamoDbMapperConfig config) { - final ConversionSchema schema = config.getConversionSchema(); - if (!cache.containsKey(schema)) { - RuleFactory rules = rulesOf(config, s3Links, this); - rules = new ConversionSchemas.ItemConverterRuleFactory(config, s3Links, rules); - cache.putIfAbsent(schema, new StandardTableFactory(rules)); - } - return cache.get(schema); - } - } - - /** - * {@link DynamoDbMapperTableModel} mapped by the clazz. - */ - private static final class StandardTableFactory implements TableFactory { - private final ConcurrentMap, DynamoDbMapperTableModel> cache; - private final RuleFactory rules; - - private StandardTableFactory(RuleFactory rules) { - this.cache = new ConcurrentHashMap, DynamoDbMapperTableModel>(); - this.rules = rules; - } - - @Override - @SuppressWarnings("unchecked") - public DynamoDbMapperTableModel getTable(Class clazz) { - if (!this.cache.containsKey(clazz)) { - this.cache.putIfAbsent(clazz, new TableBuilder(clazz, rules).build()); - } - return (DynamoDbMapperTableModel) this.cache.get(clazz); - } - } - - /** - * {@link DynamoDbMapperTableModel} builder. - */ - private static final class TableBuilder extends DynamoDbMapperTableModel.Builder { - private TableBuilder(Class clazz, Beans beans, RuleFactory rules) { - super(clazz, beans.properties()); - for (final Bean bean : beans.map().values()) { - try { - with(new FieldBuilder(clazz, bean, rules.getRule(bean.type())).build()); - } catch (final RuntimeException e) { - throw new DynamoDbMappingException(String.format( - "%s[%s] could not be mapped for type %s", - clazz.getSimpleName(), bean.properties().attributeName(), bean.type() - ), e); - } - } - } - - private TableBuilder(Class clazz, RuleFactory rules) { - this(clazz, StandardBeanProperties.of(clazz), rules); - } - } - - /** - * {@link DynamoDbMapperFieldModel} builder. - */ - private static final class FieldBuilder extends DynamoDbMapperFieldModel.Builder { - private FieldBuilder(Class clazz, Bean bean, Rule rule) { - super(clazz, bean.properties()); - if (bean.type().attributeType() != null) { - with(bean.type().attributeType()); - } else { - with(rule.getAttributeType()); - } - with(rule.newConverter(bean.type())); - with(bean.reflect()); - } - } - - /** - * Groups the conversion rules to be evaluated. - */ - private static final class Rules implements RuleFactory { - private final Set> rules = new LinkedHashSet>(); - private final DynamoDbTypeConverterFactory scalars; - - private Rules(DynamoDbTypeConverterFactory scalars) { - this.scalars = scalars; - } - - @SuppressWarnings("unchecked") - private void add(Rule rule) { - this.rules.add((Rule) rule); - } - - @Override - public Rule getRule(ConvertibleType type) { - for (final Rule rule : rules) { - if (rule.isAssignableFrom(type)) { - return rule; - } - } - return new NotSupported(); - } - - /** - * Gets the scalar converter for the given source and target types. - */ - private DynamoDbTypeConverter getConverter(Class sourceType, ConvertibleType type) { - return scalars.getConverter(sourceType, type.targetType()); - } - - /** - * Gets the nested converter for the given conversion type. - * Also wraps the resulting converter with a nullable converter. - */ - private DynamoDbTypeConverter getConverter(ConvertibleType type) { - return new DelegateConverter(getRule(type).newConverter(type)) { - public AttributeValue convert(T o) { - return o == null ? AttributeValue.builder().nul(true).build() : super.convert(o); - } - }; - } - - /** - * Native {@link AttributeValue} conversion. - */ - private class NativeType extends AbstractRule { - private NativeType(boolean supported) { - super(DynamoDbAttributeType.NULL, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.supported && type.is(AttributeValue.class); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return joinAll(type.typeConverter()); - } - - @Override - public AttributeValue get(AttributeValue o) { - return o; - } - - @Override - public void set(AttributeValue value, AttributeValue o) { - ImmutableObjectUtils.setObjectMember(value, "s", o.s()); - ImmutableObjectUtils.setObjectMember(value, "n", o.n()); - ImmutableObjectUtils.setObjectMember(value, "b", o.b()); - ImmutableObjectUtils.setObjectMember(value, "ss", o.ss()); - ImmutableObjectUtils.setObjectMember(value, "ns", o.ns()); - ImmutableObjectUtils.setObjectMember(value, "bs", o.bs()); - ImmutableObjectUtils.setObjectMember(value, "bool", o.bool()); - ImmutableObjectUtils.setObjectMember(value, "l", o.l()); - ImmutableObjectUtils.setObjectMember(value, "m", o.m()); - ImmutableObjectUtils.setObjectMember(value, "nul", o.nul()); - } - } - - /** - * {@code S} conversion - */ - private class StringScalar extends AbstractRule { - private StringScalar(boolean supported) { - super(DynamoDbAttributeType.S, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && (type.attributeType() != null || type.is(S)); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return joinAll(getConverter(String.class, type), type.typeConverter()); - } - - @Override - public String get(AttributeValue value) { - return value.s(); - } - - @Override - public void set(AttributeValue value, String o) { - ImmutableObjectUtils.setObjectMember(value, "s", o); - } - - @Override - public AttributeValue convert(String o) { - return o.length() == 0 ? null : super.convert(o); - } - } - - /** - * {@code N} conversion - */ - private class NumberScalar extends AbstractRule { - private NumberScalar(boolean supported) { - super(DynamoDbAttributeType.N, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && (type.attributeType() != null || type.is(N)); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return joinAll(getConverter(String.class, type), type.typeConverter()); - } - - @Override - public String get(AttributeValue value) { - return value.n(); - } - - @Override - public void set(AttributeValue value, String o) { - ImmutableObjectUtils.setObjectMember(value, "n", o); - //value.setN(o); - } - } - - /** - * {@code B} conversion - */ - private class BinaryScalar extends AbstractRule { - private BinaryScalar(boolean supported) { - super(DynamoDbAttributeType.B, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && (type.attributeType() != null || type.is(B)); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return joinAll(getConverter(ByteBuffer.class, type), type.typeConverter()); - } - - @Override - public ByteBuffer get(AttributeValue value) { - return value.b() == null ? null : value.b().asByteBuffer(); - } - - @Override - public void set(AttributeValue value, ByteBuffer o) { - ImmutableObjectUtils.setObjectMember(value, "b", SdkBytes.fromByteBuffer(o)); - //value.setB(o); - } - } - - /** - * {@code SS} conversion - */ - private class StringScalarSet extends AbstractRule, Collection> { - private StringScalarSet(boolean supported) { - super(DynamoDbAttributeType.SS, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && (type.attributeType() != null || type.is(S, SET)); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - return joinAll(SET.join(getConverter(String.class, type.param(0))), type.>typeConverter()); - } - - @Override - public List get(AttributeValue value) { - return value.ss(); - } - - @Override - public void set(AttributeValue value, List o) { - ImmutableObjectUtils.setObjectMember(value, "ss", o); - //value.setSS(o); - } - } - - /** - * {@code NS} conversion - */ - private class NumberScalarSet extends AbstractRule, Collection> { - private NumberScalarSet(boolean supported) { - super(DynamoDbAttributeType.NS, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && (type.attributeType() != null || type.is(N, SET)); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - return joinAll(SET.join(getConverter(String.class, type.param(0))), type.>typeConverter()); - } - - @Override - public List get(AttributeValue value) { - return value.ns(); - } - - @Override - public void set(AttributeValue value, List o) { - ImmutableObjectUtils.setObjectMember(value, "ns", o); - //value.setNS(o); - } - } - - /** - * {@code BS} conversion - */ - private class BinaryScalarSet extends AbstractRule, Collection> { - private BinaryScalarSet(boolean supported) { - super(DynamoDbAttributeType.BS, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && (type.attributeType() != null || type.is(B, SET)); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - return joinAll(SET.join(getConverter(ByteBuffer.class, type.param(0))), type.typeConverter()); - } - - @Override - public List get(AttributeValue value) { - return Optional.ofNullable(value.bs()) - .map(bs -> bs.stream() - .map(SdkBytes::asByteBuffer) - .collect(toList())) - .orElse(null); - } - - @Override - public void set(AttributeValue value, List o) { - ImmutableObjectUtils.setObjectMember(value, "bs", o.stream().map(SdkBytes::fromByteBuffer).collect(toList())); - //value.setBS(o); - } - } - - /** - * {@code SS} conversion - */ - private class ObjectStringSet extends StringScalarSet { - private ObjectStringSet(boolean supported) { - super(supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return type.attributeType() == null && super.supported && type.is(SET); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - log.warn("Marshaling a set of non-String objects to a DynamoDB " - + "StringSet. You won't be able to read these objects back " - + "out of DynamoDB unless you REALLY know what you're doing: " - + "it's probably a bug. If you DO know what you're doing feel" - + "free to ignore this warning, but consider using a custom " - + "marshaler for this instead."); - return joinAll(SET.join(scalars.getConverter(String.class, DEFAULT.type())), type.typeConverter()); - } - } - - /** - * Native boolean conversion. - */ - private class NativeBool extends AbstractRule { - private NativeBool(boolean supported) { - super(DynamoDbAttributeType.BOOL, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && type.is(BOOLEAN); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return joinAll(getConverter(Boolean.class, type), type.typeConverter()); - } - - @Override - public Boolean get(AttributeValue o) { - return o.bool(); - } - - @Override - public void set(AttributeValue o, Boolean value) { - ImmutableObjectUtils.setObjectMember(o, "bool", value); - //o.setBOOL(value); - } - - @Override - public Boolean unconvert(AttributeValue o) { - if (o.bool() == null && o.n() != null) { - return BOOLEAN.convert(o.n()); - } - return super.unconvert(o); - } - } - - /** - * Native boolean conversion. - */ - private class V2CompatibleBool extends AbstractRule { - private V2CompatibleBool(boolean supported) { - super(DynamoDbAttributeType.N, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && type.is(BOOLEAN); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return joinAll(getConverter(String.class, type), type.typeConverter()); - } - - /** - * For V2 Compatible schema we support loading booleans from a numeric attribute value (0/1) or the native boolean - * type. - */ - @Override - public String get(AttributeValue o) { - if (o.bool() != null) { - // Handle native bools, transform to expected numeric representation. - return o.bool() ? "1" : "0"; - } - return o.n(); - } - - /** - * For the V2 compatible schema we save as a numeric attribute value unless overridden by {@link - * DynamoDbNativeBoolean} or {@link DynamoDbTyped}. - */ - @Override - public void set(AttributeValue o, String value) { - ImmutableObjectUtils.setObjectMember(o, "n", value); - //o.setN(value); - } - } - - /** - * Any {@link Set} conversions. - */ - private class ObjectSet extends AbstractRule, Collection> { - private ObjectSet(boolean supported) { - super(DynamoDbAttributeType.L, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && type.param(0) != null && type.is(SET); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - return joinAll(SET.join(getConverter(type.param(0))), type.>typeConverter()); - } - - @Override - public List get(AttributeValue value) { - return value.l(); - } - - @Override - public void set(AttributeValue value, List o) { - ImmutableObjectUtils.setObjectMember(value, "l", o); - //value.setL(o); - } - } - - /** - * Native bool {@link Set} conversions. - */ - private class NativeBoolSet extends ObjectSet { - private NativeBoolSet(boolean supported) { - super(supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && type.param(0).is(BOOLEAN); - } - - @Override - public List unconvert(AttributeValue o) { - if (o.l() == null && o.ns() != null) { - return LIST.convert(o.ns(), new NativeBool(true).join(scalars.getConverter(Boolean.class, String.class))); - } - return super.unconvert(o); - } - } - - /** - * Any {@link List} conversions. - */ - private class ObjectList extends AbstractRule, List> { - private ObjectList(boolean supported) { - super(DynamoDbAttributeType.L, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && type.param(0) != null && type.is(LIST); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - return joinAll(LIST.join(getConverter(type.param(0))), type.>typeConverter()); - } - - @Override - public List get(AttributeValue value) { - return value.l(); - } - - @Override - public void set(AttributeValue value, List o) { - ImmutableObjectUtils.setObjectMember(value, "l", o); - //value.setL(o); - } - } - - /** - * Any {@link Map} conversions. - */ - private class ObjectMap extends AbstractRule, Map> { - private ObjectMap(boolean supported) { - super(DynamoDbAttributeType.M, supported); - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return super.isAssignableFrom(type) && type.param(1) != null && type.is(MAP) && type.param(0).is(STRING); - } - - @Override - public DynamoDbTypeConverter> newConverter(ConvertibleType> type) { - return joinAll( - MAP.join(getConverter(type.param(1))), - type.>typeConverter() - ); - } - - @Override - public Map get(AttributeValue value) { - return value.m(); - } - - @Override - public void set(AttributeValue value, Map o) { - ImmutableObjectUtils.setObjectMember(value, "m", o); - //value.setM(o); - } - } - - /** - * All object conversions. - */ - private class ObjectDocumentMap extends AbstractRule, T> { - private final DynamoDbMapperModelFactory models; - private final DynamoDbMapperConfig config; - - private ObjectDocumentMap(boolean supported, DynamoDbMapperModelFactory models, DynamoDbMapperConfig config) { - super(DynamoDbAttributeType.M, supported); - this.models = models; - this.config = config; - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return type.attributeType() == getAttributeType() && super.supported && !type.is(MAP); - } - - @Override - public DynamoDbTypeConverter newConverter(final ConvertibleType type) { - return joinAll(new DynamoDbTypeConverter, T>() { - public Map convert(final T o) { - return models.getTableFactory(config).getTable(type.targetType()).convert(o); - } - - public T unconvert(final Map o) { - return models.getTableFactory(config).getTable(type.targetType()).unconvert(o); - } - }, type.>typeConverter()); - } - - @Override - public Map get(AttributeValue value) { - return value.m(); - } - - @Override - public void set(AttributeValue value, Map o) { - ImmutableObjectUtils.setObjectMember(value, "m", o); - //value.setM(o); - } - } - - /** - * Default conversion when no match could be determined. - */ - private class NotSupported extends AbstractRule { - private NotSupported() { - super(DynamoDbAttributeType.NULL, false); - } - - @Override - public DynamoDbTypeConverter newConverter(ConvertibleType type) { - return this; - } - - @Override - public T get(AttributeValue value) { - throw new DynamoDbMappingException("not supported; requires @DynamoDBTyped or @DynamoDBTypeConverted"); - } - - @Override - public void set(AttributeValue value, T o) { - throw new DynamoDbMappingException("not supported; requires @DynamoDBTyped or @DynamoDBTypeConverted"); - } - } - } - - /** - * Basic attribute value conversion functions. - */ - private abstract static class AbstractRule extends AbstractConverter - implements Reflect, Rule { - protected final DynamoDbAttributeType attributeType; - protected final boolean supported; - - protected AbstractRule(DynamoDbAttributeType attributeType, boolean supported) { - this.attributeType = attributeType; - this.supported = supported; - } - - @Override - public boolean isAssignableFrom(ConvertibleType type) { - return type.attributeType() == null ? supported : type.attributeType() == attributeType; - } - - @Override - public DynamoDbAttributeType getAttributeType() { - return this.attributeType; - } - - @Override - public AttributeValue convert(final S o) { - final AttributeValue value = AttributeValue.builder().build(); - set(value, o); - return value; - } - - @Override - public S unconvert(final AttributeValue o) { - final S value = get(o); - if (value == null && o.nul() == null) { - throw new DynamoDbMappingException("expected " + attributeType + " in value " + o); - } - return value; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesOverrideTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesOverrideTest.java deleted file mode 100644 index 8358e9404815..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesOverrideTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class StandardModelFactoriesOverrideTest extends StandardModelFactoriesV2Test { - - private final DynamoDbMapperConfig config = new DynamoDbMapperConfig.Builder() - .withTypeConverterFactory(DynamoDbMapperConfig.DEFAULT.getTypeConverterFactory()) - .withConversionSchema(ConversionSchemas.v2Builder("V2Override").build()) - .build(); - - private final DynamoDbMapperModelFactory factory = StandardModelFactories.of(S3Link.Factory.of(null)); - private final DynamoDbMapperModelFactory.TableFactory models = factory.getTableFactory(config); - - @Override - protected AttributeValue convert(Class clazz, Method getter, Object value) { - final StandardAnnotationMaps.FieldMap map = StandardAnnotationMaps.of(getter, null); - return models.getTable(clazz).field(map.attributeName()).convert(value); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesTest.java deleted file mode 100644 index 88da788e305e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesTest.java +++ /dev/null @@ -1,2053 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; -import java.util.UUID; -import java.util.concurrent.TimeUnit; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel.DynamoDbAttributeType; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.services.dynamodb.pojos.AutoKeyAndVal; -import software.amazon.awssdk.services.dynamodb.pojos.Currency; -import software.amazon.awssdk.services.dynamodb.pojos.DateRange; -import software.amazon.awssdk.services.dynamodb.pojos.KeyAndVal; - -/** - * Unit tests for {@link DynamoDbMapperModelFactory.TableFactory}. - */ -public class StandardModelFactoriesTest { - - private static final DynamoDbMapperModelFactory factory = StandardModelFactories.of(S3Link.Factory.of(null)); - private static final DynamoDbMapperModelFactory.TableFactory models = factory.getTableFactory(DynamoDbMapperConfig.DEFAULT); - - @SuppressWarnings("unchecked") - private static DynamoDbMapperTableModel getTable(T object) { - return models.getTable((Class) object.getClass()); - } - - /** - * Assert that the field key properties are correct. - */ - private static void assertFieldKeyType(KeyType keyType, DynamoDbMapperFieldModel field, - DynamoDbMapperTableModel model) { - assertEquals(keyType, field.keyType()); - if (keyType != null) { - if (keyType == KeyType.HASH) { - assertEquals(field, model.hashKey()); - } else if (keyType == KeyType.RANGE) { - assertEquals(field, model.rangeKeyIfExists()); - assertEquals(field, model.rangeKey()); - } - } - } - - /** - * Assert that the field contains the LSIs. - */ - private static void assertFieldGsiNames(List names, KeyType keyType, DynamoDbMapperFieldModel field, - DynamoDbMapperTableModel model) { - assertEquals(names == null ? 0 : names.size(), field.globalSecondaryIndexNames(keyType).size()); - assertEquals(true, field.indexed()); - if (names != null) { - for (final String name : names) { - assertEquals(true, field.globalSecondaryIndexNames(keyType).contains(name)); - assertEquals(true, model.globalSecondaryIndex(name) != null); - assertEquals(true, !model.globalSecondaryIndexes().isEmpty()); - } - } - } - - /** - * Assert that the field contains the LSIs. - */ - private static void assertFieldLsiNames(List names, DynamoDbMapperFieldModel field, - DynamoDbMapperTableModel model) { - assertEquals(names == null ? 0 : names.size(), field.localSecondaryIndexNames().size()); - assertEquals(true, field.indexed()); - if (names != null) { - for (final String name : names) { - assertEquals(true, field.localSecondaryIndexNames().contains(name)); - assertEquals(true, model.localSecondaryIndex(name) != null); - assertEquals(true, !model.localSecondaryIndexes().isEmpty()); - } - } - } - - /** - * Test mappings. - */ - @Test - public void testHashAndRangeKey() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey(attributeName = "hk") - public String getKey() { - return super.getKey(); - } - - @DynamoDbRangeKey(attributeName = "rk") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertFieldKeyType(KeyType.HASH, model.field("hk"), model); - assertFieldKeyType(KeyType.RANGE, model.field("rk"), model); - } - - /** - * Test mappings. - */ - @Test(expected = DynamoDbMappingException.class) - public void testHashAndRangeKeyConflict() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey - @DynamoDbRangeKey - public String getKey() { - return super.getKey(); - } - }; - getTable(obj); - } - - /** - * Test mappings. - */ - @Test - public void testNamed() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbNamed("value") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertEquals(2, model.fields().size()); - assertNotNull(model.field("key")); - assertNotNull(model.field("value")); - } - - /** - * Test mappings. - */ - @Test - public void testAttributeTypeAsNumber() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbTyped(DynamoDbAttributeType.N) - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAttributeType.N, val.attributeType()); - } - - @Test - public void testAttributeTypeAsAttributeValueNumber() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbTyped(DynamoDbAttributeType.N) - public AttributeValue getVal() { - return super.getVal(); - } - - public void setVal(final AttributeValue val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAttributeType.N, val.attributeType()); - assertEquals("123", val.convert(AttributeValue.builder().n("123").build()).n()); - assertEquals("123", val.unconvert(AttributeValue.builder().n("123").build()).n()); - } - - @Test - public void testAttributeTypeAsAttributeValueMap() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbTyped(DynamoDbAttributeType.M) - public AttributeValue getVal() { - return super.getVal(); - } - - public void setVal(final AttributeValue val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAttributeType.M, val.attributeType()); - - Map map = new HashMap(); - map.put("A", AttributeValue.builder().n("123").build()); - map = Collections.unmodifiableMap(map); - - assertEquals("123", val.convert(AttributeValue.builder().m(map).build()).m().get("A").n()); - assertEquals("123", val.unconvert(AttributeValue.builder().m(map).build()).m().get("A").n()); - } - - /** - * Test mappings. - */ - @Test - public void testScalarAttributeStringTimeZone() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey - public String getKey() { - return super.getKey(); - } - - @DynamoDbScalarAttribute(type = ScalarAttributeType.S) - public TimeZone getVal() { - return super.getVal(); - } - - public void setVal(final TimeZone val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAttributeType.S, val.attributeType()); - assertEquals("America/New_York", val.convert(TimeZone.getTimeZone("America/New_York")).s()); - assertEquals("America/New_York", val.unconvert(AttributeValue.builder().s("America/New_York").build()).getID()); - } - - /** - * Test mappings. - */ - @Test - public void testScalarAttributeStringLocale() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey - public String getKey() { - return super.getKey(); - } - - @DynamoDbScalarAttribute(type = ScalarAttributeType.S) - public Locale getVal() { - return super.getVal(); - } - - public void setVal(final Locale val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAttributeType.S, val.attributeType()); - assertEquals("en-CA", val.convert(new Locale("en", "CA")).s()); - assertEquals("en-CA", val.unconvert(AttributeValue.builder().s("en-CA").build()).toString().replaceAll("_", "-")); - } - - /** - * Test mappings. - */ - @Test - public void testScalarAttributeBinaryUuid() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey - public String getKey() { - return super.getKey(); - } - - @DynamoDbScalarAttribute(type = ScalarAttributeType.B) - public UUID getVal() { - return super.getVal(); - } - - public void setVal(final UUID val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertEquals(DynamoDbAttributeType.B, model.field("val").attributeType()); - final UUID val = UUID.randomUUID(); - final AttributeValue converted = model.field("val").convert(val); - assertNotNull(converted.b()); - assertEquals(val, model.field("val").unconvert(converted)); - } - - @Test - public void testScalarAttributeAttributeName() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey - public String getKey() { - return super.getKey(); - } - - @DynamoDbScalarAttribute(attributeName = "value", type = ScalarAttributeType.S) - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = models.getTable((Class) obj.getClass()); - final DynamoDbMapperFieldModel val = model.field("value"); - assertEquals(DynamoDbAttributeType.S, val.attributeType()); - } - - /** - * Test mappings. - */ - @Test - public void testIgnore() { - final Object obj = new AutoKeyAndVal() { - private String ignore; - - @DynamoDbAttribute(attributeName = "value") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbIgnore - @DynamoDbAttribute(attributeName = "ignore") - public String getIgnore() { - return this.ignore; - } - - public void setIgnore(final String ignore) { - this.ignore = ignore; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertEquals(2, model.fields().size()); - assertNotNull(model.field("key")); - assertNotNull(model.field("value")); - } - - /** - * Test mappings. - */ - @Test - public void testConvertedBool() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbConvertedBool(DynamoDbConvertedBool.Format.Y_N) - public Boolean getVal() { - return super.getVal(); - } - - public void setVal(final Boolean val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAttributeType.S, val.attributeType()); - assertEquals("Y", val.convert(Boolean.TRUE).s()); - assertEquals(Boolean.TRUE, val.unconvert(AttributeValue.builder().s("Y").build())); - assertEquals("N", val.convert(Boolean.FALSE).s()); - assertEquals(Boolean.FALSE, val.unconvert(AttributeValue.builder().s("N").build())); - assertEquals(null, val.convert(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedHashKeyString() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAttribute - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel key = model.field("key"); - assertFieldKeyType(KeyType.HASH, key, model); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, key.getGenerateStrategy()); - assertNotNull(key.generate(null)); - assertNotNull(key.generate(UUID.randomUUID().toString())); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedRangeKeyUuid() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbRangeKey - @DynamoDbAutoGeneratedKey - public UUID getVal() { - return super.getVal(); - } - - public void setVal(final UUID val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertFieldKeyType(KeyType.RANGE, val, model); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertNotNull(val.generate(null)); - assertNotNull(val.generate(UUID.randomUUID())); - } - - /** - * Test mappings. - */ - @Test(expected = DynamoDbMappingException.class) - public void testAutoGeneratedConflict() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbHashKey - @DynamoDbAutoGeneratedKey - @DynamoDbVersionAttribute - public String getKey() { - return super.getKey(); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - } - - /** - * Test mappings. - */ - @Test(expected = DynamoDbMappingException.class) - public void testAutoGeneratedVersionUuid() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbVersionAttribute - public UUID getVal() { - return super.getVal(); - } - - public void setVal(final UUID val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - val.generate(null); //<- should fail - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionBigInteger() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbVersionAttribute - public BigInteger getVal() { - return super.getVal(); - } - - public void setVal(final BigInteger val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(true, val.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertEquals(BigInteger.ONE, val.generate(null)); - assertEquals(BigInteger.valueOf((int) 2), val.generate(BigInteger.ONE)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionByte() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbVersionAttribute - public Byte getVal() { - return super.getVal(); - } - - public void setVal(final Byte val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(true, val.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertEquals(Byte.valueOf((byte) 1), val.generate(null)); - assertEquals(Byte.valueOf((byte) 2), val.generate(Byte.valueOf((byte) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionBytePrimitive() { - final Object obj = new AutoKeyAndVal() { - private byte rvn; - - @DynamoDbAttribute - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbVersionAttribute - public byte getRvn() { - return this.rvn; - } - - public void setRvn(final byte rvn) { - this.rvn = rvn; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel rvn = model.field("rvn"); - assertEquals(true, rvn.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, rvn.getGenerateStrategy()); - assertEquals(Byte.valueOf((byte) 1), rvn.generate(null)); - assertEquals(Byte.valueOf((byte) 2), rvn.generate(Byte.valueOf((byte) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionInteger() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbVersionAttribute - public Integer getVal() { - return super.getVal(); - } - - public void setVal(final Integer val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(true, val.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertEquals(Integer.valueOf((int) 1), val.generate(null)); - assertEquals(Integer.valueOf((int) 2), val.generate(Integer.valueOf((int) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionIntegerPrimitive() { - final Object obj = new AutoKeyAndVal() { - private int rvn; - - @DynamoDbAttribute - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbVersionAttribute - public int getRvn() { - return this.rvn; - } - - public void setRvn(final int rvn) { - this.rvn = rvn; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel rvn = model.field("rvn"); - assertEquals(true, rvn.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, rvn.getGenerateStrategy()); - assertEquals(Integer.valueOf((int) 1), rvn.generate(null)); - assertEquals(Integer.valueOf((int) 2), rvn.generate(Integer.valueOf((int) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionLong() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbVersionAttribute - public Long getVal() { - return super.getVal(); - } - - public void setVal(final Long val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(true, val.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertEquals(Long.valueOf((long) 1), val.generate(null)); - assertEquals(Long.valueOf((long) 2), val.generate(Long.valueOf((long) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionLongPrimitive() { - final Object obj = new AutoKeyAndVal() { - private long rvn; - - @DynamoDbAttribute - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbVersionAttribute - public long getRvn() { - return this.rvn; - } - - public void setRvn(final long rvn) { - this.rvn = rvn; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel rvn = model.field("rvn"); - assertEquals(true, rvn.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, rvn.getGenerateStrategy()); - assertEquals(Long.valueOf((long) 1), rvn.generate(null)); - assertEquals(Long.valueOf((long) 2), rvn.generate(Long.valueOf((long) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionshort() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbVersionAttribute - public Short getVal() { - return super.getVal(); - } - - public void setVal(final Short val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(true, val.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertEquals(Short.valueOf((short) 1), val.generate(null)); - assertEquals(Short.valueOf((short) 2), val.generate(Short.valueOf((short) 1))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedVersionshortPrimitive() { - final Object obj = new AutoKeyAndVal() { - private short rvn; - - @DynamoDbAttribute - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbVersionAttribute - public short getRvn() { - return this.rvn; - } - - public void setRvn(final short rvn) { - this.rvn = rvn; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel rvn = model.field("rvn"); - assertEquals(true, rvn.versioned()); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, rvn.getGenerateStrategy()); - assertEquals(Short.valueOf((short) 1), rvn.generate(null)); - assertEquals(Short.valueOf((short) 2), rvn.generate(Short.valueOf((short) 1))); - } - - /** - * Test mappings. - */ - @Test(expected = DynamoDbMappingException.class) - public void testAutoGeneratedTimestampUuid() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedTimestamp - public UUID getVal() { - return super.getVal(); - } - - public void setVal(final UUID val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedTimestampCalendar() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedTimestamp - public Calendar getVal() { - return super.getVal(); - } - - public void setVal(final Calendar val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertNotNull(val.generate(null)); - assertNotNull(val.generate(Calendar.getInstance())); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedTimestampDateKey() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbRangeKey - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.CREATE) - public Date getVal() { - return super.getVal(); - } - - public void setVal(final Date val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertFieldKeyType(KeyType.RANGE, val, model); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertNotNull(val.generate(null)); - assertNotNull(val.generate(new Date())); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedTimestampDateVal() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedTimestamp - public Date getVal() { - return super.getVal(); - } - - public void setVal(final Date val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertNotNull(val.generate(null)); - assertNotNull(val.generate(new Date())); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedTimestampLong() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedTimestamp - public Long getVal() { - return super.getVal(); - } - - public void setVal(final Long val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.ALWAYS, val.getGenerateStrategy()); - assertNotNull(val.generate(null)); - assertNotNull(val.generate(System.currentTimeMillis())); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultByteBuffer() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("default-val") - public ByteBuffer getVal() { - return super.getVal(); - } - - public void setVal(final ByteBuffer val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertNotNull(val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBigDecimal() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234.5") - public BigDecimal getVal() { - return super.getVal(); - } - - public void setVal(final BigDecimal val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(BigDecimal.valueOf(1234.5D), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBigInteger() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234") - public BigInteger getVal() { - return super.getVal(); - } - - public void setVal(final BigInteger val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(BigInteger.valueOf(1234), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBoolean_true() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("true") - public Boolean getVal() { - return super.getVal(); - } - - public void setVal(final Boolean val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Boolean.TRUE, val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBoolean_0() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("0") - public Boolean getVal() { - return super.getVal(); - } - - public void setVal(final Boolean val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Boolean.FALSE, val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBoolean_1() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1") - public Boolean getVal() { - return super.getVal(); - } - - public void setVal(final Boolean val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Boolean.TRUE, val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBoolean_y() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("y") - public Boolean getVal() { - return super.getVal(); - } - - public void setVal(final Boolean val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Boolean.TRUE, val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultBoolean_Y() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("Y") - public Boolean getVal() { - return super.getVal(); - } - - public void setVal(final Boolean val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Boolean.TRUE, val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultByte() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1") - public Byte getVal() { - return super.getVal(); - } - - public void setVal(final Byte val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Byte.valueOf((byte) 1), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultCharacter() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("A") - public Character getVal() { - return super.getVal(); - } - - public void setVal(final Character val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Character.valueOf('A'), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultCurrency() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("CAD") - public java.util.Currency getVal() { - return super.getVal(); - } - - public void setVal(final java.util.Currency val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(java.util.Currency.getInstance("CAD"), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultDouble() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234.5") - public Double getVal() { - return super.getVal(); - } - - public void setVal(final Double val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Double.valueOf(1234.5D), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultEnum() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbTypeConvertedEnum - @DynamoDbAutoGeneratedDefault("SECONDS") - public TimeUnit getVal() { - return super.getVal(); - } - - public void setVal(final TimeUnit val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(TimeUnit.SECONDS, val.generate(null)); - assertEquals(TimeUnit.SECONDS, val.generate(TimeUnit.MILLISECONDS)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultFloat() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234.5") - public Float getVal() { - return super.getVal(); - } - - public void setVal(final Float val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Float.valueOf(1234.5F), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultInteger() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234") - public Integer getVal() { - return super.getVal(); - } - - public void setVal(final Integer val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Integer.valueOf((int) 1234), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultLong() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234") - public Long getVal() { - return super.getVal(); - } - - public void setVal(final Long val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Long.valueOf((long) 1234), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultShort() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("1234") - public Short getVal() { - return super.getVal(); - } - - public void setVal(final Short val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(Short.valueOf((short) 1234), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultString() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("default-val") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals("default-val", val.generate(null)); - assertEquals("default-val", val.generate("not-default")); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultTimeZone() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("America/New_York") - public TimeZone getVal() { - return super.getVal(); - } - - public void setVal(final TimeZone val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(TimeZone.getTimeZone("America/New_York"), val.generate(null)); - assertEquals(TimeZone.getTimeZone("America/New_York"), val.generate(TimeZone.getTimeZone("America/Los_Angeles"))); - } - - /** - * Test mappings. - */ - @Test - public void testAutoGeneratedDefaultUuid() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbAutoGeneratedDefault("12345678-1234-1234-1234-123456789012") - public UUID getVal() { - return super.getVal(); - } - - public void setVal(final UUID val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel val = model.field("val"); - assertEquals(DynamoDbAutoGenerateStrategy.CREATE, val.getGenerateStrategy()); - assertEquals(UUID.fromString("12345678-1234-1234-1234-123456789012"), val.generate(null)); - } - - /** - * Test mappings. - */ - @Test - public void testIndexHashKeyGlobalSecondaryIndexName() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbIndexHashKey(attributeName = "gsi_hk", globalSecondaryIndexName = "gsi") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel gsi_hk = model.field("gsi_hk"); - assertFieldGsiNames(Arrays.asList("gsi"), KeyType.HASH, gsi_hk, model); - assertFieldGsiNames(null, KeyType.RANGE, gsi_hk, model); - assertFieldLsiNames(null, gsi_hk, model); - } - - /** - * Test mappings. - */ - @Test - public void testIndexHashKeyGlobalSecondaryIndexNames() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbIndexHashKey(attributeName = "gsi_hk", globalSecondaryIndexNames = "gsi") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel gsi_hk = model.field("gsi_hk"); - assertFieldGsiNames(Arrays.asList("gsi"), KeyType.HASH, gsi_hk, model); - assertFieldGsiNames(null, KeyType.RANGE, gsi_hk, model); - assertFieldLsiNames(null, gsi_hk, model); - } - - /** - * Test mappings. - */ - @Test - public void testIndexRangeKeyGlobalSecondaryIndexName() { - final Object obj = new AutoKeyAndVal() { - private String gsi; - - @DynamoDbIndexHashKey(attributeName = "gsi_hk", globalSecondaryIndexName = "gsi") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbIndexRangeKey(attributeName = "gsi_rk", globalSecondaryIndexName = "gsi") - public String getGsi() { - return this.gsi; - } - - public void setGsi(final String gsi) { - this.gsi = gsi; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel gsi_hk = model.field("gsi_hk"); - assertFieldGsiNames(Arrays.asList("gsi"), KeyType.HASH, gsi_hk, model); - assertFieldGsiNames(null, KeyType.RANGE, gsi_hk, model); - assertFieldLsiNames(null, gsi_hk, model); - final DynamoDbMapperFieldModel gsi_rk = model.field("gsi_rk"); - assertFieldGsiNames(null, KeyType.HASH, gsi_rk, model); - assertFieldGsiNames(Arrays.asList("gsi"), KeyType.RANGE, gsi_rk, model); - assertFieldLsiNames(null, gsi_rk, model); - } - - /** - * Test mappings. - */ - @Test - public void testIndexRangeKeyGlobalSecondaryIndexNames() { - final Object obj = new AutoKeyAndVal() { - private String gsi; - - @DynamoDbIndexHashKey(attributeName = "gsi_hk", globalSecondaryIndexName = "gsi") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - - @DynamoDbIndexRangeKey(attributeName = "gsi_rk", globalSecondaryIndexNames = "gsi") - public String getGsi() { - return this.gsi; - } - - public void setGsi(final String gsi) { - this.gsi = gsi; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel gsi_hk = model.field("gsi_hk"); - assertFieldGsiNames(Arrays.asList("gsi"), KeyType.HASH, gsi_hk, model); - assertFieldGsiNames(null, KeyType.RANGE, gsi_hk, model); - assertFieldLsiNames(null, gsi_hk, model); - final DynamoDbMapperFieldModel gsi_rk = model.field("gsi_rk"); - assertFieldGsiNames(null, KeyType.HASH, gsi_rk, model); - assertFieldGsiNames(Arrays.asList("gsi"), KeyType.RANGE, gsi_rk, model); - assertFieldLsiNames(null, gsi_rk, model); - } - - /** - * Test mappings. - */ - @Test - public void testIndexRangeKeyiLocalSecondaryIndexName() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbIndexRangeKey(attributeName = "lsi_rk", localSecondaryIndexName = "lsi") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel lsi_rk = model.field("lsi_rk"); - assertFieldLsiNames(Arrays.asList("lsi"), lsi_rk, model); - } - - /** - * Test mappings. - */ - @Test - public void testIndexRangeKeyLocalSecondaryIndexNames() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbIndexRangeKey(attributeName = "lsi_rk", localSecondaryIndexNames = "lsi") - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - final DynamoDbMapperFieldModel lsi_rk = model.field("lsi_rk"); - assertFieldLsiNames(Arrays.asList("lsi"), lsi_rk, model); - } - - @Test - public void testFlattened() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "start", attributeName = "DateRangeStart"), - @DynamoDbAttribute(mappedBy = "end", attributeName = "DateRangeEnd")}) - public DateRange getVal() { - return super.getVal(); - } - - public void setVal(final DateRange val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertEquals(3, model.fields().size()); - assertEquals("DateRangeStart", model.field("DateRangeStart").name()); - assertEquals("DateRangeEnd", model.field("DateRangeEnd").name()); - } - - /** - * Test mappings. - */ - @Test - public void testFlattenedNotAllSpecified() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "start", attributeName = "DateRangeStart")}) - public DateRange getVal() { - return super.getVal(); - } - - public void setVal(final DateRange val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertEquals(2, model.fields().size()); - assertEquals("DateRangeStart", model.field("DateRangeStart").name()); - } - - /** - * Test mappings. - */ - @Test(expected = DynamoDbMappingException.class) - public void testFlattenedInvalidMappedBy() { - final Object obj = new AutoKeyAndVal() { - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "xstart", attributeName = "DateRangeStart"), - @DynamoDbAttribute(mappedBy = "xend", attributeName = "DateRangeEnd")}) - public DateRange getVal() { - return super.getVal(); - } - - public void setVal(final DateRange val) { - super.setVal(val); - } - }; - getTable(obj); - } - - /** - * Test mappings. - */ - @Test - public void testFlattenedMultipleSameType() { - final Object obj = new AutoKeyAndVal() { - private Currency other; - - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "amount", attributeName = "firstAmount"), - @DynamoDbAttribute(mappedBy = "unit", attributeName = "firstUnit")}) - public Currency getVal() { - return super.getVal(); - } - - public void setVal(final Currency val) { - super.setVal(val); - } - - @DynamoDbFlattened(attributes = { - @DynamoDbAttribute(mappedBy = "amount", attributeName = "secondAmount"), - @DynamoDbAttribute(mappedBy = "unit", attributeName = "secondUnit")}) - public Currency getOther() { - return this.other; - } - - public void setOther(final Currency other) { - this.other = other; - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - assertEquals(5, model.fields().size()); - assertEquals("firstAmount", model.field("firstAmount").name()); - assertEquals("firstUnit", model.field("firstUnit").name()); - assertEquals("secondAmount", model.field("secondAmount").name()); - assertEquals("secondUnit", model.field("secondUnit").name()); - } - - /** - * Test mappings. - */ - @Test - public void testTableAndDocument() { - models.getTable(TableAndDocument.class); - } - - /** - * Test mappings. - */ - @Test - public void testInheritedWithNoTable() { - final Object obj = new KeyAndVal() { - @DynamoDbHashKey(attributeName = "hk") - public String getKey() { - return super.getKey(); - } - - public void setKey(String key) { - super.setKey(key); - } - - @DynamoDbAttribute(attributeName = "value") - public String getVal() { - return super.getVal(); - } - - public void setVal(String val) { - super.setVal(val); - } - }; - final DynamoDbMapperTableModel model = getTable(obj); - - final DynamoDbMapperFieldModel key = model.field("hk"); - assertNotNull(key); - assertEquals(KeyType.HASH, key.keyType()); - assertEquals(DynamoDbAttributeType.S, key.attributeType()); - - final DynamoDbMapperFieldModel val = model.field("value"); - assertNotNull(val); - assertEquals(DynamoDbAttributeType.S, val.attributeType()); - } - - /** - * Test mappings to make sure the bridge method is ruled out. - */ - @Test - public void testFindRelevantGettersWithBridgeMethod() { - final DynamoDbMapperTableModel model = models.getTable(SubClass.class); - assertEquals("only two getter should be returned", 2, model.fields().size()); - assertEquals("return type should be Integer rather than Object", DynamoDbAttributeType.N, model.field("t").attributeType()); - } - - /** - * Test mappings. - */ - @Test - public void testNonMappedInheritedProperties() { - final DynamoDbMapperTableModel model = models.getTable(NonMappedInheritedProperties.class); - assertEquals(2, model.fields().size()); - assertNotNull(model.field("doUse")); - } - - /** - * Test mappings. - */ - @Test - public void testInheritedProperties() { - final DynamoDbMapperTableModel model1 = models.getTable(BaseTablePojo.class); - assertEquals(3, model1.fields().size()); - assertNotNull(model1.field("hashKeyOnField")); - assertNotNull(model1.field("rangeKeyOnGetter")); - final DynamoDbMapperTableModel model2 = models.getTable(TablePojoSubclass.class); - assertEquals(4, model2.fields().size()); - assertNotNull(model2.field("hashKeyOnField")); - assertNotNull(model2.field("rangeKeyOnGetter")); - } - - /** - * Test mappings. - */ - @Test - public void testPojoWithGetterAnnotations() { - PojoAsserts.assertAll(models.getTable(PojoWithGetterAnnotations.class)); - } - - /** - * Test mappings. - */ - @Test - public void testPojoWithFieldAnnotations() { - PojoAsserts.assertAll(models.getTable(PojoWithFieldAnnotations.class)); - } - - /** - * Test mappings. - */ - @Test - public void testPojoWithMixedAnnotations() { - PojoAsserts.assertAll(models.getTable(PojoWithMixedAnnotations.class)); - } - - /** - * Pojo field assertions. - */ - private static enum PojoAsserts { - hashKey(KeyType.HASH, null), - rangeKey(KeyType.RANGE, DynamoDbAutoGenerateStrategy.CREATE), - indexHashKey(null, null), - indexRangeKey(null, null), - actualAttrName(null, null), - versionedAttr(null, DynamoDbAutoGenerateStrategy.ALWAYS), - marshallingAttr(null, null); - private final DynamoDbAutoGenerateStrategy generateStrategy; - private final KeyType keyType; - - private PojoAsserts(final KeyType keyType, final DynamoDbAutoGenerateStrategy generateStrategy) { - this.generateStrategy = generateStrategy; - this.keyType = keyType; - } - - public static void assertAll(final DynamoDbMapperTableModel model) { - for (final PojoAsserts asserts : PojoAsserts.values()) { - final DynamoDbMapperFieldModel field = model.field(asserts.name()); - assertNotNull(field); - assertFieldKeyType(asserts.keyType, field, model); - assertEquals(asserts.generateStrategy, field.getGenerateStrategy()); - assertEquals(0, field.localSecondaryIndexNames().size()); - } - assertEquals(PojoAsserts.values().length, model.fields().size()); - } - } - - @DynamoDbDocument - @DynamoDbTable(tableName = "") - public static class TableAndDocument extends AutoKeyAndVal { - public String getVal() { - return super.getVal(); - } - - public void setVal(final String val) { - super.setVal(val); - } - } - - @DynamoDbTable(tableName = "") - private abstract static class SuperGenericClass { - private String id; - - @DynamoDbHashKey - public final String getId() { - return this.id; - } - - public final void setId(String id) { - this.id = id; - } - - public abstract T getT(); - - public abstract void setT(T t); - } - - @DynamoDbTable(tableName = "GenericString") - private static class SubClass extends SuperGenericClass { - private Integer t; - - @Override - public Integer getT() { - return t; - } - - @Override - public void setT(Integer t) { - this.t = t; - } - } - - @DynamoDbTable(tableName = "table") - private static class BaseTablePojo { - @DynamoDbHashKey - private String hashKeyOnField; - private String rangeKeyOnGetter; - private String attrNoAnnotation; - @DynamoDbIgnore - private String ignoredAttr; - - public String getHashKeyOnField() { - return hashKeyOnField; - } - - public void setHashKeyOnField(String hashKeyOnField) { - this.hashKeyOnField = hashKeyOnField; - } - - @DynamoDbRangeKey - public String getRangeKeyOnGetter() { - return rangeKeyOnGetter; - } - - public void setRangeKeyOnGetter(String rangeKeyOnGetter) { - this.rangeKeyOnGetter = rangeKeyOnGetter; - } - - public String getAttrNoAnnotation() { - return attrNoAnnotation; - } - - public void setAttrNoAnnotation(String attrNoAnnotation) { - this.attrNoAnnotation = attrNoAnnotation; - } - - public String getIgnoredAttr() { - return ignoredAttr; - } - - public void setIgnoredAttr(String ignoredAttr) { - this.ignoredAttr = ignoredAttr; - } - } - - @DynamoDbTable(tableName = "table") - private static class TablePojoSubclass extends BaseTablePojo { - private String ignoredAttr; - - @Override - public String getIgnoredAttr() { - return ignoredAttr; - } - - @Override - public void setIgnoredAttr(String ignoredAttr) { - this.ignoredAttr = ignoredAttr; - } - } - - /** - * A POJO model that uses getter annotations. - */ - @DynamoDbTable(tableName = "table") - private static class PojoWithGetterAnnotations { - private String hashKey; - private String rangeKey; - private String indexHashKey; - private String indexRangeKey; - private String annotatedAttr; - private Long versionedAttr; - private String marshallingAttr; - private String ignoredAttr; - - @DynamoDbHashKey - public String getHashKey() { - return hashKey; - } - - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - @DynamoDbRangeKey - @DynamoDbAutoGeneratedKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbIndexHashKey(globalSecondaryIndexName = "index") - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - @DynamoDbIndexRangeKey(globalSecondaryIndexName = "index") - public String getIndexRangeKey() { - return indexRangeKey; - } - - public void setIndexRangeKey(String indexRangeKey) { - this.indexRangeKey = indexRangeKey; - } - - @DynamoDbAttribute(attributeName = "actualAttrName") - public String getAnnotatedAttr() { - return annotatedAttr; - } - - public void setAnnotatedAttr(String annotatedAttr) { - this.annotatedAttr = annotatedAttr; - } - - @DynamoDbVersionAttribute - public Long getVersionedAttr() { - return versionedAttr; - } - - public void setVersionedAttr(Long versionedAttr) { - this.versionedAttr = versionedAttr; - } - - @DynamoDbTypeConverted(converter = RandomUuidMarshaller.class) - public String getMarshallingAttr() { - return marshallingAttr; - } - - public void setMarshallingAttr(String marshallingAttr) { - this.marshallingAttr = marshallingAttr; - } - - @DynamoDbIgnore - public String getIgnoredAttr() { - return ignoredAttr; - } - - public void setIgnoredAttr(String ignoredAttr) { - this.ignoredAttr = ignoredAttr; - } - } - - /** - * The same model as defined in PojoWithGetterAnnotations, but uses field - * annotations instead. - */ - @DynamoDbTable(tableName = "table") - private static class PojoWithFieldAnnotations { - @DynamoDbHashKey - private String hashKey; - @DynamoDbRangeKey - @DynamoDbAutoGeneratedKey - private String rangeKey; - @DynamoDbIndexHashKey(globalSecondaryIndexName = "index") - private String indexHashKey; - @DynamoDbIndexRangeKey(globalSecondaryIndexName = "index") - private String indexRangeKey; - @DynamoDbAttribute(attributeName = "actualAttrName") - private String annotatedAttr; - @DynamoDbVersionAttribute - private Long versionedAttr; - @DynamoDbTypeConverted(converter = RandomUuidMarshaller.class) - private String marshallingAttr; - @DynamoDbIgnore - private String ignoredAttr; - - public String getHashKey() { - return hashKey; - } - - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - public String getIndexRangeKey() { - return indexRangeKey; - } - - public void setIndexRangeKey(String indexRangeKey) { - this.indexRangeKey = indexRangeKey; - } - - public String getAnnotatedAttr() { - return annotatedAttr; - } - - public void setAnnotatedAttr(String annotatedAttr) { - this.annotatedAttr = annotatedAttr; - } - - public Long getVersionedAttr() { - return versionedAttr; - } - - public void setVersionedAttr(Long versionedAttr) { - this.versionedAttr = versionedAttr; - } - - public String getMarshallingAttr() { - return marshallingAttr; - } - - public void setMarshallingAttr(String marshallingAttr) { - this.marshallingAttr = marshallingAttr; - } - - public String getIgnoredAttr() { - return ignoredAttr; - } - - public void setIgnoredAttr(String ignoredAttr) { - this.ignoredAttr = ignoredAttr; - } - } - - /** - * The same model as defined in PojoWithGetterAnnotations, but uses both getter and field - * annotations. - */ - @DynamoDbTable(tableName = "table") - private static class PojoWithMixedAnnotations { - @DynamoDbHashKey - private String hashKey; - private String rangeKey; - @DynamoDbIndexHashKey(globalSecondaryIndexName = "index") - private String indexHashKey; - private String indexRangeKey; - @DynamoDbAttribute(attributeName = "actualAttrName") - private String annotatedAttr; - private Long versionedAttr; - @DynamoDbTypeConverted(converter = RandomUuidMarshaller.class) - private String marshallingAttr; - private String ignoredAttr; - - public String getHashKey() { - return hashKey; - } - - public void setHashKey(String hashKey) { - this.hashKey = hashKey; - } - - @DynamoDbRangeKey - @DynamoDbAutoGeneratedKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - @DynamoDbIndexRangeKey(globalSecondaryIndexName = "index") - public String getIndexRangeKey() { - return indexRangeKey; - } - - public void setIndexRangeKey(String indexRangeKey) { - this.indexRangeKey = indexRangeKey; - } - - public String getAnnotatedAttr() { - return annotatedAttr; - } - - public void setAnnotatedAttr(String annotatedAttr) { - this.annotatedAttr = annotatedAttr; - } - - @DynamoDbVersionAttribute - public Long getVersionedAttr() { - return versionedAttr; - } - - public void setVersionedAttr(Long versionedAttr) { - this.versionedAttr = versionedAttr; - } - - public String getMarshallingAttr() { - return marshallingAttr; - } - - public void setMarshallingAttr(String marshallingAttr) { - this.marshallingAttr = marshallingAttr; - } - - @DynamoDbIgnore - public String getIgnoredAttr() { - return ignoredAttr; - } - - public void setIgnoredAttr(String ignoredAttr) { - this.ignoredAttr = ignoredAttr; - } - } - - public abstract class AbstractNonMappedInheritedProperties { - private String doNotUse; - - public String getDoNotUse() { - return this.doNotUse; - } - - public void setDoNotUse(final String doNotUse) { - this.doNotUse = doNotUse; - } - } - - @DynamoDbTable(tableName = "aws-java-sdk-test") - public class NonMappedInheritedProperties extends AbstractNonMappedInheritedProperties { - private String id; - private String doUse; - - @DynamoDbHashKey - public final String getId() { - return this.id; - } - - public final void setId(String id) { - this.id = id; - } - - public String getDoUse() { - return this.doUse; - } - - public void setDoUse(final String doUse) { - this.doUse = doUse; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV1Test.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV1Test.java deleted file mode 100644 index 94b53ba96582..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV1Test.java +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import java.lang.reflect.Method; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.LinkedHashSet; -import java.util.TreeSet; -import java.util.UUID; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.SubClass; -import software.amazon.awssdk.services.dynamodb.pojos.TestClass; -import software.amazon.awssdk.services.dynamodb.pojos.UnannotatedSubClass; - -public class StandardModelFactoriesV1Test { - - protected static final DynamoDbMapperConfig CONFIG = new DynamoDbMapperConfig.Builder() - .withTypeConverterFactory(DynamoDbMapperConfig.DEFAULT.getTypeConverterFactory()) - .withConversionSchema(ConversionSchemas.V1) - .build(); - - private static final DynamoDbMapperModelFactory factory = StandardModelFactories.of(S3Link.Factory.of(null)); - private static final DynamoDbMapperModelFactory.TableFactory models = factory.getTableFactory(CONFIG); - - protected AttributeValue convert(Class clazz, Method getter, Object value) { - final StandardAnnotationMaps.FieldMap map = StandardAnnotationMaps.of(getter, null); - return models.getTable(clazz).field(map.attributeName()).convert(value); - } - - @Test - public void testBoolean() { - assertEquals("1", convert("getBoolean", true).n()); - assertEquals("0", convert("getBoolean", false).n()); - assertEquals("1", convert("getBoxedBoolean", true).n()); - assertEquals("0", convert("getBoxedBoolean", false).n()); - - assertEquals(true, convert("getNativeBoolean", true).bool()); - assertEquals(false, convert("getNativeBoolean", false).bool()); - } - - @Test - public void testString() { - assertEquals("abc", convert("getString", "abc").s()); - - assertEquals(RandomUuidMarshaller.randomUUID, - convert("getCustomString", "abc").s()); - } - - @Test - public void testUuid() { - UUID uuid = UUID.randomUUID(); - assertEquals(uuid.toString(), convert("getUuid", uuid).s()); - } - - @Test - public void testDate() { - assertEquals("1970-01-01T00:00:00.001Z", - convert("getDate", new Date(1)).s()); - - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(1); - - assertEquals("1970-01-01T00:00:00.001Z", - convert("getCalendar", c).s()); - } - - @Test - public void testNumbers() { - assertEquals("0", convert("getByte", (byte) 0).n()); - assertEquals("1", convert("getByte", (byte) 1).n()); - assertEquals("0", convert("getBoxedByte", (byte) 0).n()); - assertEquals("1", convert("getBoxedByte", (byte) 1).n()); - - assertEquals("0", convert("getShort", (short) 0).n()); - assertEquals("1", convert("getShort", (short) 1).n()); - assertEquals("0", convert("getBoxedShort", (short) 0).n()); - assertEquals("1", convert("getBoxedShort", (short) 1).n()); - - assertEquals("0", convert("getInt", 0).n()); - assertEquals("1", convert("getInt", 1).n()); - assertEquals("0", convert("getBoxedInt", 0).n()); - assertEquals("1", convert("getBoxedInt", 1).n()); - - assertEquals("0", convert("getLong", 0l).n()); - assertEquals("1", convert("getLong", 1l).n()); - assertEquals("0", convert("getBoxedLong", 0l).n()); - assertEquals("1", convert("getBoxedLong", 1l).n()); - - assertEquals("0", convert("getBigInt", BigInteger.ZERO).n()); - assertEquals("1", convert("getBigInt", BigInteger.ONE).n()); - - assertEquals("0.0", convert("getFloat", 0f).n()); - assertEquals("1.0", convert("getFloat", 1f).n()); - assertEquals("0.0", convert("getBoxedFloat", 0f).n()); - assertEquals("1.0", convert("getBoxedFloat", 1f).n()); - - assertEquals("0.0", convert("getDouble", 0d).n()); - assertEquals("1.0", convert("getDouble", 1d).n()); - assertEquals("0.0", convert("getBoxedDouble", 0d).n()); - assertEquals("1.0", convert("getBoxedDouble", 1d).n()); - - assertEquals("0", convert("getBigDecimal", BigDecimal.ZERO).n()); - assertEquals("1", convert("getBigDecimal", BigDecimal.ONE).n()); - } - - @Test - public void testBinary() { - SdkBytes value = SdkBytes.fromUtf8String("value"); - assertEquals(value, convert("getByteArray", value.asByteArray()).b()); - assertEquals(value, convert("getByteBuffer", value.asByteBuffer()).b()); - } - - @Test - public void testBooleanSet() { - assertEquals(Collections.singletonList("1"), - convert("getBooleanSet", Collections.singleton(true)).ns()); - - assertEquals(Collections.singletonList("0"), - convert("getBooleanSet", Collections.singleton(false)).ns()); - - assertEquals(Arrays.asList("0", "1"), - convert("getBooleanSet", new TreeSet() {{ - add(true); - add(false); - }}).ns()); - } - - @Test - public void testStringSet() { - assertEquals(Collections.singletonList("a"), - convert("getStringSet", Collections.singleton("a")).ss()); - assertEquals(Collections.singletonList("b"), - convert("getStringSet", Collections.singleton("b")).ss()); - - assertEquals(Arrays.asList("a", "b", "c"), - convert("getStringSet", new TreeSet() {{ - add("a"); - add("b"); - add("c"); - }}).ss()); - } - - @Test - public void testUuidSet() { - final UUID one = UUID.randomUUID(); - final UUID two = UUID.randomUUID(); - final UUID three = UUID.randomUUID(); - - assertEquals(Collections.singletonList(one.toString()), - convert("getUuidSet", Collections.singleton(one)).ss()); - - assertEquals(Collections.singletonList(two.toString()), - convert("getUuidSet", Collections.singleton(two)).ss()); - - assertEquals( - Arrays.asList( - one.toString(), - two.toString(), - three.toString()), - convert("getUuidSet", new LinkedHashSet() {{ - add(one); - add(two); - add(three); - }}).ss()); - } - - @Test - public void testDateSet() { - assertEquals(Collections.singletonList("1970-01-01T00:00:00.001Z"), - convert("getDateSet", Collections.singleton(new Date(1))) - .ss()); - - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(1); - - assertEquals(Collections.singletonList("1970-01-01T00:00:00.001Z"), - convert("getCalendarSet", Collections.singleton(c)) - .ss()); - } - - @Test - public void testNumberSet() { - assertEquals(Collections.singletonList("0"), - convert("getByteSet", Collections.singleton((byte) 0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getShortSet", Collections.singleton((short) 0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getIntSet", Collections.singleton(0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getLongSet", Collections.singleton(0l)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getBigIntegerSet", Collections.singleton(BigInteger.ZERO)) - .ns()); - assertEquals(Collections.singletonList("0.0"), - convert("getFloatSet", Collections.singleton(0f)).ns()); - assertEquals(Collections.singletonList("0.0"), - convert("getDoubleSet", Collections.singleton(0d)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getBigDecimalSet", Collections.singleton(BigDecimal.ZERO)) - .ns()); - - assertEquals(Arrays.asList("0", "1", "2"), - convert("getLongSet", new TreeSet() {{ - add(0); - add(1); - add(2); - }}).ns()); - } - - @Test - public void testBinarySet() { - SdkBytes test = SdkBytes.fromUtf8String("test"); - SdkBytes test2 = SdkBytes.fromUtf8String("test2"); - - assertEquals(Collections.singletonList(test), - convert("getByteArraySet", Collections.singleton(test.asByteArray())).bs()); - - assertEquals(Collections.singletonList(test), - convert("getByteBufferSet", Collections.singleton(test.asByteBuffer())).bs()); - - assertEquals(Arrays.asList(test, test2), - convert("getByteBufferSet", new TreeSet() {{ - add(test.asByteBuffer()); - add(test2.asByteBuffer()); - }}).bs()); - } - - @Test - public void testObjectSet() { - Object o = new Object() { - @Override - public String toString() { - return "hello"; - } - }; - - assertEquals(Collections.singletonList("hello"), - convert("getObjectSet", Collections.singleton(o)).ss()); - } - - @Test - public void testList() { - try { - convert("getList", Arrays.asList("a", "b", "c")); - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testMap() { - try { - convert("getMap", Collections.singletonMap("a", "b")); - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testObject() { - try { - convert("getObject", new SubClass()); - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testUnannotatedObject() throws Exception { - try { - convert(UnannotatedSubClass.class, UnannotatedSubClass.class.getMethod("getChild"), - new UnannotatedSubClass()); - - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testS3Link() { - S3ClientCache cache = new S3ClientCache((AwsCredentialsProvider) null); - S3Link link = new S3Link(cache, "bucket", "key"); - - assertEquals("{\"s3\":{" - + "\"bucket\":\"bucket\"," - + "\"key\":\"key\"," - + "\"region\":null}}", - convert("getS3Link", link).s()); - } - - private AttributeValue convert(String getter, Object value) { - try { - return convert(TestClass.class, TestClass.class.getMethod(getter), value); - - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2CompatibleTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2CompatibleTest.java deleted file mode 100644 index b967a91f0e31..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2CompatibleTest.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import java.lang.reflect.Method; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TimeZone; -import java.util.TreeSet; -import java.util.UUID; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.SubClass; -import software.amazon.awssdk.services.dynamodb.pojos.TestClass; -import software.amazon.awssdk.services.dynamodb.pojos.UnannotatedSubClass; - -public class StandardModelFactoriesV2CompatibleTest { - - protected static final DynamoDbMapperConfig CONFIG = new DynamoDbMapperConfig.Builder() - .withTypeConverterFactory(DynamoDbMapperConfig.DEFAULT.getTypeConverterFactory()) - .withConversionSchema(ConversionSchemas.V2_COMPATIBLE) - .build(); - - private static final DynamoDbMapperModelFactory factory = StandardModelFactories.of(S3Link.Factory.of(null)); - private static final DynamoDbMapperModelFactory.TableFactory models = factory.getTableFactory(CONFIG); - - protected AttributeValue convert(Class clazz, Method getter, Object value) { - final StandardAnnotationMaps.FieldMap map = StandardAnnotationMaps.of(getter, null); - return models.getTable(clazz).field(map.attributeName()).convert(value); - } - - @Test - public void testBoolean() { - assertEquals("1", convert("getBoolean", true).n()); - assertEquals("0", convert("getBoolean", false).n()); - assertEquals("1", convert("getBoxedBoolean", true).n()); - assertEquals("0", convert("getBoxedBoolean", false).n()); - - assertEquals(true, convert("getNativeBoolean", true).bool()); - assertEquals(false, convert("getNativeBoolean", false).bool()); - } - - @Test - public void testString() { - assertEquals("abc", convert("getString", "abc").s()); - - assertEquals(RandomUuidMarshaller.randomUUID, - convert("getCustomString", "abc").s()); - } - - @Test - public void testUuid() { - UUID uuid = UUID.randomUUID(); - assertEquals(uuid.toString(), convert("getUuid", uuid).s()); - } - - @Test - public void testDate() { - assertEquals("1970-01-01T00:00:00.001Z", - convert("getDate", new Date(1)).s()); - } - - @Test - public void testCalendar() { - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(0); - c.setTimeZone(TimeZone.getTimeZone("Z")); - - assertEquals("1970-01-01T00:00:00Z", - convert("getCalendar", c).s()); - } - - @Test - public void testNumbers() { - assertEquals("0", convert("getByte", (byte) 0).n()); - assertEquals("1", convert("getByte", (byte) 1).n()); - assertEquals("0", convert("getBoxedByte", (byte) 0).n()); - assertEquals("1", convert("getBoxedByte", (byte) 1).n()); - - assertEquals("0", convert("getShort", (short) 0).n()); - assertEquals("1", convert("getShort", (short) 1).n()); - assertEquals("0", convert("getBoxedShort", (short) 0).n()); - assertEquals("1", convert("getBoxedShort", (short) 1).n()); - - assertEquals("0", convert("getInt", 0).n()); - assertEquals("1", convert("getInt", 1).n()); - assertEquals("0", convert("getBoxedInt", 0).n()); - assertEquals("1", convert("getBoxedInt", 1).n()); - - assertEquals("0", convert("getLong", 0l).n()); - assertEquals("1", convert("getLong", 1l).n()); - assertEquals("0", convert("getBoxedLong", 0l).n()); - assertEquals("1", convert("getBoxedLong", 1l).n()); - - assertEquals("0", convert("getBigInt", BigInteger.ZERO).n()); - assertEquals("1", convert("getBigInt", BigInteger.ONE).n()); - - assertEquals("0.0", convert("getFloat", 0f).n()); - assertEquals("1.0", convert("getFloat", 1f).n()); - assertEquals("0.0", convert("getBoxedFloat", 0f).n()); - assertEquals("1.0", convert("getBoxedFloat", 1f).n()); - - assertEquals("0.0", convert("getDouble", 0d).n()); - assertEquals("1.0", convert("getDouble", 1d).n()); - assertEquals("0.0", convert("getBoxedDouble", 0d).n()); - assertEquals("1.0", convert("getBoxedDouble", 1d).n()); - - assertEquals("0", convert("getBigDecimal", BigDecimal.ZERO).n()); - assertEquals("1", convert("getBigDecimal", BigDecimal.ONE).n()); - } - - @Test - public void testBinary() { - SdkBytes value = SdkBytes.fromUtf8String("value"); - assertEquals(value, convert("getByteArray", value.asByteArray()).b()); - assertEquals(value, convert("getByteBuffer", value.asByteBuffer()).b()); - } - - @Test - public void testBooleanSet() { - assertEquals(Collections.singletonList("1"), - convert("getBooleanSet", Collections.singleton(true)).ns()); - - assertEquals(Collections.singletonList("0"), - convert("getBooleanSet", Collections.singleton(false)).ns()); - - assertEquals(Arrays.asList("0", "1"), - convert("getBooleanSet", new TreeSet() {{ - add(true); - add(false); - }}).ns()); - } - - @Test - public void testStringSet() { - assertEquals(Collections.singletonList("a"), - convert("getStringSet", Collections.singleton("a")).ss()); - assertEquals(Collections.singletonList("b"), - convert("getStringSet", Collections.singleton("b")).ss()); - - assertEquals(Arrays.asList("a", "b", "c"), - convert("getStringSet", new TreeSet() {{ - add("a"); - add("b"); - add("c"); - }}).ss()); - } - - @Test - public void testUuidSet() { - final UUID one = UUID.randomUUID(); - final UUID two = UUID.randomUUID(); - final UUID three = UUID.randomUUID(); - - assertEquals(Collections.singletonList(one.toString()), - convert("getUuidSet", Collections.singleton(one)).ss()); - - assertEquals(Collections.singletonList(two.toString()), - convert("getUuidSet", Collections.singleton(two)).ss()); - - assertEquals( - Arrays.asList( - one.toString(), - two.toString(), - three.toString()), - convert("getUuidSet", new LinkedHashSet() {{ - add(one); - add(two); - add(three); - }}).ss()); - } - - @Test - public void testDateSet() { - assertEquals(Collections.singletonList("1970-01-01T00:00:00.001Z"), - convert("getDateSet", Collections.singleton(new Date(1))) - .ss()); - - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(1); - - assertEquals(Collections.singletonList("1970-01-01T00:00:00.001Z"), - convert("getCalendarSet", Collections.singleton(c)) - .ss()); - } - - @Test - public void testNumberSet() { - assertEquals(Collections.singletonList("0"), - convert("getByteSet", Collections.singleton((byte) 0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getShortSet", Collections.singleton((short) 0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getIntSet", Collections.singleton(0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getLongSet", Collections.singleton(0l)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getBigIntegerSet", Collections.singleton(BigInteger.ZERO)) - .ns()); - assertEquals(Collections.singletonList("0.0"), - convert("getFloatSet", Collections.singleton(0f)).ns()); - assertEquals(Collections.singletonList("0.0"), - convert("getDoubleSet", Collections.singleton(0d)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getBigDecimalSet", Collections.singleton(BigDecimal.ZERO)) - .ns()); - - assertEquals(Arrays.asList("0", "1", "2"), - convert("getLongSet", new TreeSet() {{ - add(0); - add(1); - add(2); - }}).ns()); - } - - @Test - public void testBinarySet() { - SdkBytes test = SdkBytes.fromUtf8String("test"); - SdkBytes test2 = SdkBytes.fromUtf8String("test2"); - - assertEquals(Collections.singletonList(test), - convert("getByteArraySet", Collections.singleton(test.asByteArray())).bs()); - - assertEquals(Collections.singletonList(test), - convert("getByteBufferSet", Collections.singleton(test.asByteBuffer())).bs()); - - assertEquals(Arrays.asList(test, test2), - convert("getByteBufferSet", new TreeSet() {{ - add(test.asByteBuffer()); - add(test2.asByteBuffer()); - }}).bs()); - } - - @Test - public void testObjectSet() { - Object o = new Object() { - @Override - public String toString() { - return "hello"; - } - }; - - assertEquals(Collections.singletonList("hello"), - convert("getObjectSet", Collections.singleton(o)).ss()); - } - - @Test - public void testList() { - assertEquals(Arrays.asList( - AttributeValue.builder().s("a").build(), - AttributeValue.builder().s("b").build(), - AttributeValue.builder().s("c").build()), - convert("getList", Arrays.asList("a", "b", "c")).l()); - - assertEquals(Arrays.asList(AttributeValue.builder().nul(true).build()), - convert("getList", Collections.singletonList(null)).l()); - } - - @Test - public void testSetList() { - assertEquals(Arrays.asList( - AttributeValue.builder().ss("a").build(), - AttributeValue.builder().ss("b").build(), - AttributeValue.builder().ss("c").build()), - convert("getSetList", Arrays.asList( - Collections.singleton("a"), - Collections.singleton("b"), - Collections.singleton("c"))).l()); - } - - @Test - public void testMap() { - assertEquals(new HashMap() {{ - put("a", AttributeValue.builder().s("b").build()); - put("c", AttributeValue.builder().s("d").build()); - put("e", AttributeValue.builder().s("f").build()); - }}, - convert("getMap", new HashMap() {{ - put("a", "b"); - put("c", "d"); - put("e", "f"); - }}).m()); - - assertEquals(Collections.singletonMap("a", AttributeValue.builder().nul(true).build()), - convert("getMap", Collections.singletonMap("a", null)).m()); - } - - @Test - public void testSetMap() { - assertEquals(new HashMap() {{ - put("a", AttributeValue.builder().ss("a", "b").build()); - }}, - convert("getSetMap", new HashMap>() {{ - put("a", new TreeSet(Arrays.asList("a", "b"))); - }}).m()); - - assertEquals(new HashMap() {{ - put("a", AttributeValue.builder().ss("a").build()); - put("b", AttributeValue.builder().nul(true).build()); - }}, - convert("getSetMap", new HashMap>() {{ - put("a", new TreeSet(Arrays.asList("a"))); - put("b", null); - }}).m()); - } - - @Test - public void testObject() { - assertEquals(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - }}, - convert("getObject", new SubClass()).m()); - } - - @Test - public void testUnannotatedObject() throws Exception { - try { - convert(UnannotatedSubClass.class, UnannotatedSubClass.class.getMethod("getChild"), - new UnannotatedSubClass()); - - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testS3Link() { - S3ClientCache cache = new S3ClientCache((AwsCredentialsProvider) null); - S3Link link = new S3Link(cache, "bucket", "key"); - - assertEquals("{\"s3\":{" - + "\"bucket\":\"bucket\"," - + "\"key\":\"key\"," - + "\"region\":null}}", - convert("getS3Link", link).s()); - } - - private AttributeValue convert(String getter, Object value) { - try { - - return convert(TestClass.class, TestClass.class.getMethod(getter), value); - - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2Test.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2Test.java deleted file mode 100644 index e0d60beb151f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2Test.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import java.lang.reflect.Method; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; -import org.junit.Assert; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.SubClass; -import software.amazon.awssdk.services.dynamodb.pojos.TestClass; -import software.amazon.awssdk.services.dynamodb.pojos.UnannotatedSubClass; - -public class StandardModelFactoriesV2Test { - - protected static final DynamoDbMapperConfig CONFIG = new DynamoDbMapperConfig.Builder() - .withTypeConverterFactory(DynamoDbMapperConfig.DEFAULT.getTypeConverterFactory()) - .withConversionSchema(ConversionSchemas.V2) - .build(); - - private static final DynamoDbMapperModelFactory factory = StandardModelFactories.of(S3Link.Factory.of(null)); - private static final DynamoDbMapperModelFactory.TableFactory models = factory.getTableFactory(CONFIG); - - protected AttributeValue convert(Class clazz, Method getter, Object value) { - final StandardAnnotationMaps.FieldMap map = StandardAnnotationMaps.of(getter, null); - return models.getTable(clazz).field(map.attributeName()).convert(value); - } - - @Test - public void testBoolean() { - // These are all native booleans by default in the v2 schema - assertEquals(true, convert("getBoolean", true).bool()); - assertEquals(false, convert("getBoolean", false).bool()); - assertEquals(true, convert("getBoxedBoolean", true).bool()); - assertEquals(false, convert("getBoxedBoolean", false).bool()); - assertEquals(true, convert("getNativeBoolean", true).bool()); - assertEquals(false, convert("getNativeBoolean", false).bool()); - } - - @Test - public void testString() { - assertEquals("abc", convert("getString", "abc").s()); - - assertEquals(RandomUuidMarshaller.randomUUID, - convert("getCustomString", "abc").s()); - } - - @Test - public void testUuid() { - UUID uuid = UUID.randomUUID(); - assertEquals(uuid.toString(), convert("getUuid", uuid).s()); - } - - @Test - public void testDate() { - assertEquals("1970-01-01T00:00:00Z", - convert("getDate", new Date(0)).s()); - - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(0); - - assertEquals("1970-01-01T00:00:00Z", - convert("getCalendar", c).s()); - } - - @Test - public void testNumbers() { - assertEquals("0", convert("getByte", (byte) 0).n()); - assertEquals("1", convert("getByte", (byte) 1).n()); - assertEquals("0", convert("getBoxedByte", (byte) 0).n()); - assertEquals("1", convert("getBoxedByte", (byte) 1).n()); - - assertEquals("0", convert("getShort", (short) 0).n()); - assertEquals("1", convert("getShort", (short) 1).n()); - assertEquals("0", convert("getBoxedShort", (short) 0).n()); - assertEquals("1", convert("getBoxedShort", (short) 1).n()); - - assertEquals("0", convert("getInt", 0).n()); - assertEquals("1", convert("getInt", 1).n()); - assertEquals("0", convert("getBoxedInt", 0).n()); - assertEquals("1", convert("getBoxedInt", 1).n()); - - assertEquals("0", convert("getLong", 0l).n()); - assertEquals("1", convert("getLong", 1l).n()); - assertEquals("0", convert("getBoxedLong", 0l).n()); - assertEquals("1", convert("getBoxedLong", 1l).n()); - - assertEquals("0", convert("getBigInt", BigInteger.ZERO).n()); - assertEquals("1", convert("getBigInt", BigInteger.ONE).n()); - - assertEquals("0.0", convert("getFloat", 0f).n()); - assertEquals("1.0", convert("getFloat", 1f).n()); - assertEquals("0.0", convert("getBoxedFloat", 0f).n()); - assertEquals("1.0", convert("getBoxedFloat", 1f).n()); - - assertEquals("0.0", convert("getDouble", 0d).n()); - assertEquals("1.0", convert("getDouble", 1d).n()); - assertEquals("0.0", convert("getBoxedDouble", 0d).n()); - assertEquals("1.0", convert("getBoxedDouble", 1d).n()); - - assertEquals("0", convert("getBigDecimal", BigDecimal.ZERO).n()); - assertEquals("1", convert("getBigDecimal", BigDecimal.ONE).n()); - } - - @Test - public void testBinary() { - SdkBytes value = SdkBytes.fromUtf8String("value"); - assertEquals(value, convert("getByteArray", value.asByteArray()).b()); - assertEquals(value, convert("getByteBuffer", value.asByteBuffer()).b()); - } - - @Test - public void testBooleanSet() { - // Set (which is silly but technically valid) gets mapped to - // a List of Booleans now via the ObjectSetToListMarshaller. - AttributeValue value = - convert("getBooleanSet", Collections.singleton(true)); - - Assert.assertEquals(1, value.l().size()); - Assert.assertEquals(true, value.l().get(0).bool()); - } - - @Test - public void testStringSet() { - assertEquals(Collections.singletonList("a"), - convert("getStringSet", Collections.singleton("a")).ss()); - assertEquals(Collections.singletonList("b"), - convert("getStringSet", Collections.singleton("b")).ss()); - - assertEquals(Arrays.asList("a", "b", "c"), - convert("getStringSet", new TreeSet() {{ - add("a"); - add("b"); - add("c"); - }}).ss()); - } - - @Test - public void testUuidSet() { - final UUID one = UUID.randomUUID(); - final UUID two = UUID.randomUUID(); - final UUID three = UUID.randomUUID(); - - assertEquals(Collections.singletonList(one.toString()), - convert("getUuidSet", Collections.singleton(one)).ss()); - - assertEquals(Collections.singletonList(two.toString()), - convert("getUuidSet", Collections.singleton(two)).ss()); - - assertEquals( - Arrays.asList( - one.toString(), - two.toString(), - three.toString()), - convert("getUuidSet", new LinkedHashSet() {{ - add(one); - add(two); - add(three); - }}).ss()); - } - - @Test - public void testDateSet() { - assertEquals(Collections.singletonList("1970-01-01T00:00:00Z"), - convert("getDateSet", Collections.singleton(new Date(0))) - .ss()); - - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(0); - - assertEquals(Collections.singletonList("1970-01-01T00:00:00Z"), - convert("getCalendarSet", Collections.singleton(c)) - .ss()); - } - - @Test - public void testNumberSet() { - assertEquals(Collections.singletonList("0"), - convert("getByteSet", Collections.singleton((byte) 0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getShortSet", Collections.singleton((short) 0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getIntSet", Collections.singleton(0)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getLongSet", Collections.singleton(0l)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getBigIntegerSet", Collections.singleton(BigInteger.ZERO)) - .ns()); - assertEquals(Collections.singletonList("0.0"), - convert("getFloatSet", Collections.singleton(0f)).ns()); - assertEquals(Collections.singletonList("0.0"), - convert("getDoubleSet", Collections.singleton(0d)).ns()); - assertEquals(Collections.singletonList("0"), - convert("getBigDecimalSet", Collections.singleton(BigDecimal.ZERO)) - .ns()); - - assertEquals(Arrays.asList("0", "1", "2"), - convert("getLongSet", new TreeSet() {{ - add(0); - add(1); - add(2); - }}).ns()); - } - - @Test - public void testBinarySet() { - SdkBytes test = SdkBytes.fromUtf8String("test"); - SdkBytes test2 = SdkBytes.fromUtf8String("test2"); - - assertEquals(Collections.singletonList(test), - convert("getByteArraySet", Collections.singleton(test.asByteArray())).bs()); - - assertEquals(Collections.singletonList(test), - convert("getByteBufferSet", Collections.singleton(test.asByteBuffer())).bs()); - - assertEquals(Arrays.asList(test, test2), - convert("getByteBufferSet", new TreeSet() {{ - add(test.asByteBuffer()); - add(test2.asByteBuffer()); - }}).bs()); - } - - @Test - public void testObjectSet() { - AttributeValue value = - convert("getObjectSet", Collections.singleton(new SubClass())); - - assertEquals(1, value.l().size()); - assertEquals(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - }}, - value.l().get(0).m()); - - assertEquals(Arrays.asList(AttributeValue.builder().nul(true).build()), - convert("getObjectSet", Collections.singleton(null)).l()); - } - - @Test - public void testList() { - assertEquals(Arrays.asList( - AttributeValue.builder().s("a").build(), - AttributeValue.builder().s("b").build(), - AttributeValue.builder().s("c").build()), - convert("getList", Arrays.asList("a", "b", "c")).l()); - - assertEquals(Arrays.asList(AttributeValue.builder().nul(true).build()), - convert("getList", Collections.singletonList(null)).l()); - } - - @Test - public void testObjectList() { - AttributeValue value = convert( - "getObjectList", - Collections.singletonList(new SubClass())); - - assertEquals(1, value.l().size()); - assertEquals(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - }}, - value.l().get(0).m()); - } - - @Test - public void testSetList() { - assertEquals( - Arrays.asList(AttributeValue.builder().ss("a").build()), - convert("getSetList", Arrays.asList( - Collections.singleton("a"))).l()); - - List> list = new ArrayList>(); - list.add(null); - - assertEquals( - Arrays.asList(AttributeValue.builder().nul(true).build()), - convert("getSetList", list).l()); - } - - @Test - public void testMap() { - assertEquals(new HashMap() {{ - put("a", AttributeValue.builder().s("b").build()); - put("c", AttributeValue.builder().s("d").build()); - put("e", AttributeValue.builder().s("f").build()); - }}, - convert("getMap", new HashMap() {{ - put("a", "b"); - put("c", "d"); - put("e", "f"); - }}).m()); - - assertEquals(Collections.singletonMap("a", AttributeValue.builder().nul(true).build()), - convert("getMap", Collections.singletonMap("a", null)).m()); - } - - @Test - public void testSetMap() { - assertEquals(new HashMap() {{ - put("a", AttributeValue.builder().ss("a", "b").build()); - }}, - convert("getSetMap", new HashMap>() {{ - put("a", new TreeSet(Arrays.asList("a", "b"))); - }}).m()); - - assertEquals(new HashMap() {{ - put("a", AttributeValue.builder().ss("a").build()); - put("b", AttributeValue.builder().nul(true).build()); - }}, - convert("getSetMap", new HashMap>() {{ - put("a", new TreeSet(Arrays.asList("a"))); - put("b", null); - }}).m()); - } - - @Test - public void testObject() { - assertEquals(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - }}, - convert("getObject", new SubClass()).m()); - } - - @Test - public void testUnannotatedObject() throws Exception { - try { - convert(UnannotatedSubClass.class, UnannotatedSubClass.class.getMethod("getChild"), - new UnannotatedSubClass()); - - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testS3Link() { - S3ClientCache cache = new S3ClientCache((AwsCredentialsProvider) null); - S3Link link = new S3Link(cache, "bucket", "key"); - - assertEquals("{\"s3\":{" - + "\"bucket\":\"bucket\"," - + "\"key\":\"key\"," - + "\"region\":null}}", - convert("getS3Link", link).s()); - } - - private AttributeValue convert(String getter, Object value) { - try { - - return convert(TestClass.class, TestClass.class.getMethod(getter), value); - - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2UnconvertTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2UnconvertTest.java deleted file mode 100644 index 9e73b4583dc0..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardModelFactoriesV2UnconvertTest.java +++ /dev/null @@ -1,545 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; - -import java.lang.reflect.Method; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.time.Instant; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.TreeSet; -import java.util.UUID; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.pojos.SubClass; -import software.amazon.awssdk.services.dynamodb.pojos.TestClass; -import software.amazon.awssdk.services.dynamodb.pojos.UnannotatedSubClass; - -public class StandardModelFactoriesV2UnconvertTest { - - protected static final DynamoDbMapperConfig CONFIG = new DynamoDbMapperConfig.Builder() - .withTypeConverterFactory(DynamoDbMapperConfig.DEFAULT.getTypeConverterFactory()) - .withConversionSchema(ConversionSchemas.V2) - .build(); - - private static final DynamoDbMapperModelFactory factory = StandardModelFactories.of(new S3Link.Factory(new S3ClientCache((AwsCredentialsProvider) null))); - private static final DynamoDbMapperModelFactory.TableFactory models = factory.getTableFactory(CONFIG); - - protected Object unconvert(Class clazz, Method getter, Method setter, AttributeValue value) { - final StandardAnnotationMaps.FieldMap map = StandardAnnotationMaps.of(getter, null); - return models.getTable(clazz).field(map.attributeName()).unconvert(value); - } - - @Test - public void testBoolean() { - assertEquals(false, unconvert("getBoolean", "setBoolean", - AttributeValue.builder().n("0").build())); - - assertEquals(true, unconvert("getBoolean", "setBoolean", - AttributeValue.builder().n("1").build())); - - assertEquals(false, unconvert("getBoolean", "setBoolean", - AttributeValue.builder().bool(false).build())); - - assertEquals(true, unconvert("getBoolean", "setBoolean", - AttributeValue.builder().bool(true).build())); - - assertEquals(false, unconvert("getBoxedBoolean", "setBoxedBoolean", - AttributeValue.builder().n("0").build())); - - assertEquals(true, unconvert("getBoxedBoolean", "setBoxedBoolean", - AttributeValue.builder().n("1").build())); - - assertEquals(false, unconvert("getBoxedBoolean", "setBoxedBoolean", - AttributeValue.builder().bool(false).build())); - - assertEquals(true, unconvert("getBoxedBoolean", "setBoxedBoolean", - AttributeValue.builder().bool(true).build())); - } - - @Test - public void testString() { - assertEquals("test", unconvert("getString", "setString", - AttributeValue.builder().s("test").build())); - - Assert.assertNull(unconvert("getCustomString", "setCustomString", - AttributeValue.builder().s("ignoreme").build())); - } - - @Test - public void testUuid() { - UUID uuid = UUID.randomUUID(); - assertEquals(uuid, unconvert("getUuid", "setUuid", - AttributeValue.builder().s(uuid.toString()).build())); - } - - @Test - public void testDate() { - Date date = Date.from(Instant.ofEpochMilli(0)); - assertEquals(date, unconvert("getDate", "setDate", - AttributeValue.builder().s("1970-01-01T00:00:00Z").build())); - } - - @Test - public void testCalendar() { - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(1); - - assertEquals(c, unconvert("getCalendar", "setCalendar", - AttributeValue.builder().s("1970-01-01T00:00:00.001Z").build())); - } - - @Test - public void testNumbers() { - assertEquals((byte) 1, unconvert("getByte", "setByte", - AttributeValue.builder().n("1").build())); - assertEquals((byte) 1, unconvert("getBoxedByte", "setBoxedByte", - AttributeValue.builder().n("1").build())); - - assertEquals((short) 1, unconvert("getShort", "setShort", - AttributeValue.builder().n("1").build())); - assertEquals((short) 1, unconvert("getBoxedShort", "setBoxedShort", - AttributeValue.builder().n("1").build())); - - assertEquals(1, unconvert("getInt", "setInt", - AttributeValue.builder().n("1").build())); - assertEquals(1, unconvert("getBoxedInt", "setBoxedInt", - AttributeValue.builder().n("1").build())); - - assertEquals(1l, unconvert("getLong", "setLong", - AttributeValue.builder().n("1").build())); - assertEquals(1l, unconvert("getBoxedLong", "setBoxedLong", - AttributeValue.builder().n("1").build())); - - assertEquals(BigInteger.ONE, unconvert("getBigInt", "setBigInt", - AttributeValue.builder().n("1").build())); - - assertEquals(1.5f, unconvert("getFloat", "setFloat", - AttributeValue.builder().n("1.5").build())); - assertEquals(1.5f, unconvert("getBoxedFloat", "setBoxedFloat", - AttributeValue.builder().n("1.5").build())); - - assertEquals(1.5d, unconvert("getDouble", "setDouble", - AttributeValue.builder().n("1.5").build())); - assertEquals(1.5d, unconvert("getBoxedDouble", "setBoxedDouble", - AttributeValue.builder().n("1.5").build())); - - assertEquals(BigDecimal.ONE, unconvert("getBigDecimal", "setBigDecimal", - AttributeValue.builder().n("1").build())); - } - - @Test - public void testBinary() { - SdkBytes test = SdkBytes.fromUtf8String("test"); - assertArrayEquals(test.asByteArray(), - (byte[]) unconvert("getByteArray", "setByteArray", AttributeValue.builder().b(test).build())); - - assertEquals(test.asByteBuffer(), - unconvert("getByteBuffer", "setByteBuffer", AttributeValue.builder().b(test).build())); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testBooleanSet() { - assertEquals(new HashSet() {{ - add(true); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().ns("1").build())); - - assertEquals(new HashSet() {{ - add(false); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().ns("0").build())); - - assertEquals(new HashSet() {{ - add(true); - add(false); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().ns("0", "1").build())); - - assertEquals(new HashSet() {{ - add(true); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().l( - AttributeValue.builder().bool(true).build()).build())); - - assertEquals(new HashSet() {{ - add(false); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().l( - AttributeValue.builder().bool(false).build()).build())); - - assertEquals(new HashSet() {{ - add(false); - add(true); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().l( - AttributeValue.builder().bool(false).build(), - AttributeValue.builder().bool(true).build()).build())); - - assertEquals(new HashSet() {{ - add(null); - }}, - unconvert("getBooleanSet", "setBooleanSet", - AttributeValue.builder().l( - AttributeValue.builder().nul(true).build()).build())); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testStringSet() { - Assert.assertNull(unconvert("getStringSet", "setStringSet", - AttributeValue.builder().nul(true).build())); - - assertEquals(new HashSet() {{ - add("a"); - add("b"); - }}, - unconvert("getStringSet", "setStringSet", - AttributeValue.builder().ss("a", "b").build())); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testUuidSet() { - Assert.assertNull(unconvert("getUuidSet", "setUuidSet", - AttributeValue.builder().nul(true).build())); - - final UUID one = UUID.randomUUID(); - final UUID two = UUID.randomUUID(); - - assertEquals(new HashSet() {{ - add(one); - add(two); - }}, - unconvert("getUuidSet", "setUuidSet", - AttributeValue.builder().ss( - one.toString(), - two.toString()).build())); - } - - @Test - public void testDateSet() { - assertEquals(Collections.singleton(new Date(0)), - unconvert("getDateSet", "setDateSet", AttributeValue.builder() - .ss("1970-01-01T00:00:00.000Z").build())); - - Calendar c = GregorianCalendar.getInstance(); - c.setTimeInMillis(0); - - assertEquals(Collections.singleton(c), - unconvert("getCalendarSet", "setCalendarSet", - AttributeValue.builder() - .ss("1970-01-01T00:00:00.000Z").build())); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testNumberSet() { - Assert.assertNull(unconvert("getByteSet", "setByteSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getShortSet", "setShortSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getIntSet", "setIntSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getLongSet", "setLongSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getBigIntegerSet", "setBigIntegerSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getFloatSet", "setFloatSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getDoubleSet", "setDoubleSet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getBigDecimalSet", "setBigDecimalSet", - AttributeValue.builder().nul(true).build())); - - - assertEquals(new HashSet() {{ - add((byte) 1); - }}, - unconvert("getByteSet", "setByteSet", - AttributeValue.builder().ns("1").build())); - - assertEquals(new HashSet() {{ - add((short) 1); - }}, - unconvert("getShortSet", "setShortSet", - AttributeValue.builder().ns("1").build())); - - assertEquals(new HashSet() {{ - add(1); - }}, - unconvert("getIntSet", "setIntSet", - AttributeValue.builder().ns("1").build())); - - assertEquals(new HashSet() {{ - add(1l); - }}, - unconvert("getLongSet", "setLongSet", - AttributeValue.builder().ns("1").build())); - - assertEquals(new HashSet() {{ - add(BigInteger.ONE); - }}, - unconvert("getBigIntegerSet", "setBigIntegerSet", - AttributeValue.builder().ns("1").build())); - - assertEquals(new HashSet() {{ - add(1.5f); - }}, - unconvert("getFloatSet", "setFloatSet", - AttributeValue.builder().ns("1.5").build())); - - assertEquals(new HashSet() {{ - add(1.5d); - }}, - unconvert("getDoubleSet", "setDoubleSet", - AttributeValue.builder().ns("1.5").build())); - - assertEquals(new HashSet() {{ - add(BigDecimal.ONE); - }}, - unconvert("getBigDecimalSet", "setBigDecimalSet", - AttributeValue.builder().ns("1").build())); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testBinarySet() { - Assert.assertNull(unconvert("getByteArraySet", "setByteArraySet", - AttributeValue.builder().nul(true).build())); - Assert.assertNull(unconvert("getByteBufferSet", "setByteBufferSet", - AttributeValue.builder().nul(true).build())); - - ByteBuffer test = ByteBuffer.wrap("test".getBytes()); - - Set result = (Set) unconvert( - "getByteArraySet", "setByteArraySet", - AttributeValue.builder().bs(SdkBytes.fromByteBuffer(test.slice())).build()); - - assertEquals(1, result.size()); - Assert.assertTrue(Arrays.equals( - "test".getBytes(), - result.iterator().next())); - - Assert.assertEquals(Collections.singleton(test.slice()), - unconvert("getByteBufferSet", "setByteBufferSet", - AttributeValue.builder().bs(SdkBytes.fromByteBuffer(test.slice())).build())); - } - - @Test - @Ignore // No longer works because the converters aren't aware of auto construct maps - public void testObjectSet() { - Object result = unconvert("getObjectSet", "setObjectSet", - AttributeValue.builder().l(AttributeValue.builder().m( - new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - put("null", AttributeValue.builder().nul(true).build()); - }} - ) - .build()) - .build()); - - assertEquals(Collections.singleton(new SubClass()), result); - - result = unconvert("getObjectSet", "setObjectSet", - AttributeValue.builder().l( - AttributeValue.builder() - .nul(true) - .build()) - .build()); - - assertEquals(Collections.singleton(null), result); - } - - @Test - @Ignore // No longer works because the converters aren't aware of auto construct maps - public void testList() { - Assert.assertNull(unconvert("getList", "setList", - AttributeValue.builder().nul(true).build())); - - assertEquals(Arrays.asList("a", "b", "c"), - unconvert("getList", "setList", AttributeValue.builder().l( - AttributeValue.builder().s("a").build(), - AttributeValue.builder().s("b").build(), - AttributeValue.builder().s("c").build()) - .build())); - - assertEquals(Arrays.asList("a", null), - unconvert("getList", "setList", AttributeValue.builder().l( - AttributeValue.builder().s("a").build(), - AttributeValue.builder().nul(true).build()).build())); - } - - @Test - @Ignore // No longer works because the converters aren't aware of auto construct maps - public void testObjectList() { - Assert.assertNull(unconvert("getObjectList", "setObjectList", - AttributeValue.builder().nul(true).build())); - - assertEquals(Arrays.asList(new SubClass(), null), - unconvert("getObjectList", "setObjectList", - AttributeValue.builder().l( - AttributeValue.builder().m(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - put("null", AttributeValue.builder().nul(true).build()); - }}).build(), - AttributeValue.builder().nul(true).build()).build())); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testSetList() { - Assert.assertNull(unconvert("getSetList", "setSetList", - AttributeValue.builder().nul(true).build())); - - assertEquals(Arrays.asList(new Set[] {null}), - unconvert("getSetList", "setSetList", AttributeValue.builder().l( - AttributeValue.builder().nul(true).build()).build())); - - assertEquals(Arrays.asList(Collections.singleton("a")), - unconvert("getSetList", "setSetList", AttributeValue.builder().l( - AttributeValue.builder().ss("a").build()).build())); - } - - @Test - @Ignore // No longer works because the converters aren't aware of auto construct maps - public void testMap() { - Assert.assertNull(unconvert("getMap", "setMap", - AttributeValue.builder().nul(true).build())); - - assertEquals(new HashMap() {{ - put("a", "b"); - put("c", "d"); - }}, - unconvert("getMap", "setMap", AttributeValue.builder().m( - new HashMap() {{ - put("a", AttributeValue.builder().s("b").build()); - put("c", AttributeValue.builder().s("d").build()); - }}).build())); - - assertEquals(new HashMap() {{ - put("a", null); - }}, - unconvert("getMap", "setMap", AttributeValue.builder().m( - new HashMap() {{ - put("a", AttributeValue.builder().nul(true).build()); - }}).build())); - } - - @Test - @Ignore // No longer works because the converters aren't aware of auto construct maps - public void testSetMap() { - Assert.assertNull(unconvert("getSetMap", "setSetMap", - AttributeValue.builder().nul(true).build())); - - assertEquals(new HashMap>() {{ - put("a", null); - put("b", new TreeSet(Arrays.asList("a", "b"))); - }}, - unconvert("getSetMap", "setSetMap", AttributeValue.builder().m( - new HashMap() {{ - put("a", AttributeValue.builder().nul(true).build()); - put("b", AttributeValue.builder().ss("a", "b").build()); - }}).build())); - } - - @Test - @Ignore // No longer works because the converters aren't aware of auto construct maps - public void testObject() { - Assert.assertNull(unconvert("getObject", "setObject", - AttributeValue.builder().nul(true).build())); - - assertEquals(new SubClass(), unconvert("getObject", "setObject", - AttributeValue.builder().m(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - }}).build())); - - assertEquals(new SubClass(), unconvert("getObject", "setObject", - AttributeValue.builder().m(new HashMap() {{ - put("name", AttributeValue.builder().s("name").build()); - put("value", AttributeValue.builder().n("123").build()); - put("null", AttributeValue.builder().nul(true).build()); - }}).build())); - } - - @Test - public void testUnannotatedObject() throws Exception { - Method getter = UnannotatedSubClass.class.getMethod("getChild"); - Method setter = UnannotatedSubClass.class - .getMethod("setChild", UnannotatedSubClass.class); - - try { - unconvert(UnannotatedSubClass.class, getter, setter, AttributeValue.builder().s("").build()); - Assert.fail("Expected DynamoDBMappingException"); - } catch (DynamoDbMappingException e) { - // Ignored or expected. - } - } - - @Test - public void testS3Link() { - S3Link link = (S3Link) unconvert("getS3Link", "setS3Link", - AttributeValue.builder().s("{\"s3\":{" - + "\"bucket\":\"bucket\"," - + "\"key\":\"key\"," - + "\"region\":null}}").build()); - - assertEquals("bucket", link.bucketName()); - assertEquals("key", link.getKey()); - assertEquals(Region.US_EAST_1, link.s3Region()); - } - - public Object unconvert(String getter, String setter, AttributeValue value) { - try { - - Method gm = TestClass.class.getMethod(getter); - Method sm = TestClass.class.getMethod(setter, gm.getReturnType()); - return unconvert(TestClass.class, gm, sm, value); - - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException("BOOM", e); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardTypeConverters.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardTypeConverters.java deleted file mode 100644 index 43b9cbb3e0ce..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StandardTypeConverters.java +++ /dev/null @@ -1,1137 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static java.time.format.DateTimeFormatter.ISO_DATE_TIME; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Currency; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.regex.Pattern; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.utils.DateUtils; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -/** - * Type conversions. - * - * @see DynamoDbTypeConverter - */ -@SdkInternalApi -final class StandardTypeConverters extends DynamoDbTypeConverterFactory { - - /** - * Standard scalar type-converter factory. - */ - private static final DynamoDbTypeConverterFactory FACTORY = new StandardTypeConverters(); - - static DynamoDbTypeConverterFactory factory() { - return StandardTypeConverters.FACTORY; - } - - /** - * {@inheritDoc} - */ - @Override - public DynamoDbTypeConverter getConverter(Class sourceType, Class targetType) { - final Scalar source = Scalar.of(sourceType); - final Scalar target = Scalar.of(targetType); - final Converter toSource = source.getConverter(sourceType, target.type()); - final Converter toTarget = target.getConverter(targetType, source.type()); - return new StandardDynamoDbTypeConverter<>(toSource, toTarget); - } - - /** - * Standard scalar types. - */ - enum Scalar { - /** - * {@link BigDecimal} - */ - BIG_DECIMAL(ScalarAttributeType.N, new ConverterMap(BigDecimal.class, null) - .with(Number.class, ToBigDecimal.FROM_STRING.join(ToString.FROM_NUMBER)) - .with(String.class, ToBigDecimal.FROM_STRING) - ), - - /** - * {@link BigInteger} - */ - BIG_INTEGER(ScalarAttributeType.N, new ConverterMap(BigInteger.class, null) - .with(Number.class, ToBigInteger.FROM_STRING.join(ToString.FROM_NUMBER)) - .with(String.class, ToBigInteger.FROM_STRING) - ), - - /** - * {@link Boolean} - */ - BOOLEAN(ScalarAttributeType.N, new ConverterMap(Boolean.class, Boolean.TYPE) - .with(Number.class, ToBoolean.FROM_STRING.join(ToString.FROM_NUMBER)) - .with(String.class, ToBoolean.FROM_STRING) - ), - - /** - * {@link Byte} - */ - BYTE(ScalarAttributeType.N, new ConverterMap(Byte.class, Byte.TYPE) - .with(Number.class, ToByte.FROM_NUMBER) - .with(String.class, ToByte.FROM_STRING) - ), - - /** - * {@link Byte} array - */ - BYTE_ARRAY(ScalarAttributeType.B, new ConverterMap(byte[].class, null) - .with(ByteBuffer.class, ToByteArray.FROM_BYTE_BUFFER) - .with(String.class, ToByteArray.FROM_STRING) - ), - - /** - * {@link ByteBuffer} - */ - BYTE_BUFFER(ScalarAttributeType.B, new ConverterMap(ByteBuffer.class, null) - .with(byte[].class, ToByteBuffer.FROM_BYTE_ARRAY) - .with(String.class, ToByteBuffer.FROM_BYTE_ARRAY.join(ToByteArray.FROM_STRING)) - .with(java.util.UUID.class, ToByteBuffer.FROM_UUID) - ), - - /** - * {@link Calendar} - */ - CALENDAR(ScalarAttributeType.S, new ConverterMap(Calendar.class, null) - .with(Date.class, ToCalendar.FROM_DATE) - .with(Instant.class, ToCalendar.FROM_INSTANT) - .with(ZonedDateTime.class, ToCalendar.FROM_INSTANT.join(ToInstant.FROM_ZONEDDATETIME)) - .with(Long.class, ToCalendar.FROM_INSTANT.join(ToInstant.FROM_LONG)) - .with(String.class, ToCalendar.FROM_INSTANT.join(ToInstant.FROM_STRING)) - ), - - /** - * {@link Character} - */ - CHARACTER(ScalarAttributeType.S, new ConverterMap(Character.class, Character.TYPE) - .with(String.class, ToCharacter.FROM_STRING) - ), - - /** - * {@link Currency} - */ - CURRENCY(ScalarAttributeType.S, new ConverterMap(Currency.class, null) - .with(String.class, ToCurrency.FROM_STRING) - ), - - /** - * {@link Instant} - */ - INSTANT(ScalarAttributeType.S, new ConverterMap(Instant.class, null) - .with(Calendar.class, ToInstant.FROM_CALENDAR) - .with(ZonedDateTime.class, ToInstant.FROM_ZONEDDATETIME) - .with(Long.class, ToInstant.FROM_LONG) - .with(String.class, ToInstant.FROM_STRING) - ), - /** - * {@link Date} - */ - DATE(ScalarAttributeType.S, new ConverterMap(Date.class, null) - .with(Instant.class, ToDate.FROM_INSTANT) - .with(Calendar.class, ToDate.FROM_CALENDAR) - .with(ZonedDateTime.class, ToDate.FROM_INSTANT.join(ToInstant.FROM_ZONEDDATETIME)) - .with(Long.class, ToDate.FROM_INSTANT.join(ToInstant.FROM_LONG)) - .with(String.class, ToDate.FROM_STRING) - ), - - /** - * {@link ZonedDateTime} - */ - ZONED_DATE_TIME(/*ScalarAttributeType.S*/null, new ConverterMap(ZonedDateTime.class, null) - .with(Calendar.class, ToDateTime.FROM_CALENDAR) - .with(Date.class, ToDateTime.FROM_DATE) - .with(Long.class, ToDateTime.FROM_INSTANT_AT_UTC.join(ToInstant.FROM_LONG)) - .with(String.class, ToDateTime.FROM_INSTANT_AT_UTC.join(ToInstant.FROM_STRING)) - ), - - /** - * {@link Double} - */ - DOUBLE(ScalarAttributeType.N, new ConverterMap(Double.class, Double.TYPE) - .with(Number.class, ToDouble.FROM_NUMBER) - .with(String.class, ToDouble.FROM_STRING) - ), - - /** - * {@link Float} - */ - FLOAT(ScalarAttributeType.N, new ConverterMap(Float.class, Float.TYPE) - .with(Number.class, ToFloat.FROM_NUMBER) - .with(String.class, ToFloat.FROM_STRING) - ), - - /** - * {@link Integer} - */ - INTEGER(ScalarAttributeType.N, new ConverterMap(Integer.class, Integer.TYPE) - .with(Number.class, ToInteger.FROM_NUMBER) - .with(String.class, ToInteger.FROM_STRING) - ), - - /** - * {@link Locale} - */ - LOCALE(ScalarAttributeType.S, new ConverterMap(Locale.class, null) - .with(String.class, ToLocale.FROM_STRING) - ), - - /** - * {@link Long} - */ - LONG(ScalarAttributeType.N, new ConverterMap(Long.class, Long.TYPE) - .with(Date.class, ToLong.FROM_DATE) - .with(ZonedDateTime.class, ToLong.FROM_TEMPORAL_ACCESSOR.join(ToInstant.FROM_ZONEDDATETIME)) - .with(Number.class, ToLong.FROM_NUMBER) - .with(String.class, ToLong.FROM_STRING) - ), - - /** - * {@link S3Link} - */ - S3_LINK(ScalarAttributeType.S, new ConverterMap(S3Link.class, null)), - - /** - * {@link Short} - */ - SHORT(ScalarAttributeType.N, new ConverterMap(Short.class, Short.TYPE) - .with(Number.class, ToShort.FROM_NUMBER) - .with(String.class, ToShort.FROM_STRING) - ), - - /** - * {@link String} - */ - STRING(ScalarAttributeType.S, new ConverterMap(String.class, null) - .with(Boolean.class, ToString.FROM_BOOLEAN) - .with(byte[].class, ToString.FROM_BYTE_ARRAY) - .with(ByteBuffer.class, ToString.FROM_BYTE_ARRAY.join(ToByteArray.FROM_BYTE_BUFFER)) - .with(Calendar.class, ToString.FROM_DATE.join(ToDate.FROM_CALENDAR)) - .with(Date.class, ToString.FROM_DATE) - .with(ZonedDateTime.class, ToString.FROM_DATE_TIME) - .with(Enum.class, ToString.FROM_ENUM) - .with(Locale.class, ToString.FROM_LOCALE) - .with(TimeZone.class, ToString.FROM_TIME_ZONE) - .with(ZoneId.class, ToString.FROM_ZONE_ID) - .with(Object.class, ToString.FROM_OBJECT) - ), - - /** - * {@link TimeZone} - */ - TIME_ZONE(ScalarAttributeType.S, new ConverterMap(TimeZone.class, null) - .with(String.class, ToTimeZone.FROM_STRING) - ), - /** - * {@link ZoneId} - */ - ZONE_ID(ScalarAttributeType.S, new ConverterMap(ZoneId.class, null) - .with(String.class, ToZoneId.FROM_STRING) - ), - - /** - * {@link java.net.URL} - */ - URL(ScalarAttributeType.S, new ConverterMap(java.net.URL.class, null) - .with(String.class, ToUrl.FROM_STRING) - ), - - /** - * {@link java.net.URI} - */ - URI(ScalarAttributeType.S, new ConverterMap(java.net.URI.class, null) - .with(String.class, ToUri.FROM_STRING) - ), - - /** - * {@link java.util.UUID} - */ - UUID(ScalarAttributeType.S, new ConverterMap(java.util.UUID.class, null) - .with(ByteBuffer.class, ToUuid.FROM_BYTE_BUFFER) - .with(String.class, ToUuid.FROM_STRING) - ), - - /** - * {@link Object}; default must be last - */ - DEFAULT(null, new ConverterMap(Object.class, null)) { - @Override - Converter getConverter(Class sourceType, Class targetType) { - if (sourceType.isEnum() && STRING.map.isAssignableFrom(targetType)) { - return (Converter) new ToEnum.FromString(sourceType); - } - return super.getConverter(sourceType, targetType); - } - }; - - /** - * The scalar attribute type. - */ - private final ScalarAttributeType scalarAttributeType; - - /** - * The mapping of conversion functions for this scalar. - */ - private final ConverterMap map; - - /** - * Constructs a new scalar with the specified conversion mappings. - */ - Scalar(ScalarAttributeType scalarAttributeType, ConverterMap map) { - this.scalarAttributeType = scalarAttributeType; - this.map = map; - } - - /** - * Returns the first matching scalar, which may be the same as or a - * supertype of the specified target class. - */ - static Scalar of(Class type) { - for (final Scalar scalar : Scalar.values()) { - if (scalar.is(type)) { - return scalar; - } - } - return DEFAULT; - } - - /** - * Returns the function to convert from the specified target class to - * this scalar type. - */ - Converter getConverter(Class sourceType, Class targetType) { - return map.getConverter(targetType); - } - - /** - * Converts the target instance using the standard type-conversions. - */ - @SuppressWarnings("unchecked") - S convert(Object o) { - return getConverter(this.type(), (Class) o.getClass()).convert(o); - } - - /** - * Determines if the scalar is of the specified scalar attribute type. - */ - boolean is(final ScalarAttributeType scalarAttributeType) { - return this.scalarAttributeType == scalarAttributeType; - } - - /** - * Determines if the class represented by this scalar is either the - * same as or a supertype of the specified target type. - */ - boolean is(final Class type) { - return this.map.isAssignableFrom(type); - } - - /** - * Returns the primary reference type. - */ - @SuppressWarnings("unchecked") - Class type() { - return (Class) this.map.referenceType; - } - } - - /** - * Standard vector types. - */ - abstract static class Vector { - /** - * {@link List} - */ - static final ToList LIST = new ToList(); - /** - * {@link Map} - */ - static final ToMap MAP = new ToMap(); - /** - * {@link Set} - */ - static final ToSet SET = new ToSet(); - - /** - * Determines if the class represented by this vector is either the - * same as or a supertype of the specified target type. - */ - abstract boolean is(Class type); - - static final class ToList extends Vector { - DynamoDbTypeConverter, List> join(final DynamoDbTypeConverter scalar) { - return new ListTypeConverter<>(scalar); - } - - List convert(Collection o, DynamoDbTypeConverter scalar) { - final List vector = new ArrayList(o.size()); - for (final T t : o) { - vector.add(scalar.convert(t)); - } - return vector; - } - - List unconvert(Collection o, DynamoDbTypeConverter scalar) { - final List vector = new ArrayList(o.size()); - for (final S s : o) { - vector.add(scalar.unconvert(s)); - } - return vector; - } - - @Override - boolean is(final Class type) { - return List.class.isAssignableFrom(type); - } - - private static class ListTypeConverter implements DynamoDbTypeConverter, List> { - private final DynamoDbTypeConverter scalar; - - ListTypeConverter(DynamoDbTypeConverter scalar) { - this.scalar = scalar; - } - - @Override - public final List convert(final List o) { - return LIST.convert(o, scalar); - } - - @Override - public final List unconvert(final List o) { - return LIST.unconvert(o, scalar); - } - } - } - - static final class ToMap extends Vector { - DynamoDbTypeConverter, Map> join(final DynamoDbTypeConverter scalar) { - return new MapTypeConverter<>(scalar); - } - - Map convert(Map o, DynamoDbTypeConverter scalar) { - final Map vector = new LinkedHashMap(); - for (final Map.Entry t : o.entrySet()) { - vector.put(t.getKey(), scalar.convert(t.getValue())); - } - return vector; - } - - Map unconvert(Map o, DynamoDbTypeConverter scalar) { - final Map vector = new LinkedHashMap(); - for (final Map.Entry s : o.entrySet()) { - vector.put(s.getKey(), scalar.unconvert(s.getValue())); - } - return vector; - } - - boolean is(final Class type) { - return Map.class.isAssignableFrom(type); - } - - private static class MapTypeConverter implements DynamoDbTypeConverter, Map> { - private final DynamoDbTypeConverter scalar; - - MapTypeConverter(DynamoDbTypeConverter scalar) { - this.scalar = scalar; - } - - @Override - public final Map convert(final Map o) { - return MAP.convert(o, scalar); - } - - @Override - public final Map unconvert(final Map o) { - return MAP.unconvert(o, scalar); - } - } - } - - static final class ToSet extends Vector { - DynamoDbTypeConverter, Collection> join(final DynamoDbTypeConverter target) { - return new SetTypeConverter<>(target); - } - - Set unconvert(Collection o, DynamoDbTypeConverter scalar) { - final Set vector = new LinkedHashSet(); - for (final S s : o) { - if (vector.add(scalar.unconvert(s)) == false) { - throw new DynamoDbMappingException("duplicate value (" + s + ")"); - } - } - return vector; - } - - boolean is(final Class type) { - return Set.class.isAssignableFrom(type); - } - - private static class SetTypeConverter implements DynamoDbTypeConverter, Collection> { - private final DynamoDbTypeConverter target; - - SetTypeConverter(DynamoDbTypeConverter target) { - this.target = target; - } - - @Override - public List convert(final Collection o) { - return LIST.convert(o, target); - } - - @Override - public Collection unconvert(final List o) { - return SET.unconvert(o, target); - } - } - } - } - - /** - * Converter map. - */ - private static class ConverterMap extends LinkedHashMap, Converter> { - private static final long serialVersionUID = -1L; - private final Class referenceType; - private final Class primitiveType; - - private ConverterMap(Class referenceType, Class primitiveType) { - this.referenceType = referenceType; - this.primitiveType = primitiveType; - } - - private ConverterMap with(Class targetType, Converter converter) { - put(targetType, converter); - return this; - } - - private boolean isAssignableFrom(Class type) { - return type.isPrimitive() ? primitiveType == type : referenceType.isAssignableFrom(type); - } - - @SuppressWarnings("unchecked") - private Converter getConverter(Class targetType) { - for (final Map.Entry, Converter> entry : entrySet()) { - if (entry.getKey().isAssignableFrom(targetType)) { - return (Converter) entry.getValue(); - } - } - if (isAssignableFrom(targetType)) { - return (Converter) ToObject.FROM_OBJECT; - } - throw new DynamoDbMappingException( - "type [" + targetType + "] is not supported; no conversion from " + referenceType - ); - } - } - - /** - * {@link BigDecimal} conversion functions. - */ - private abstract static class ToBigDecimal extends Converter { - private static final ToBigDecimal FROM_STRING = new ToBigDecimal() { - @Override - public BigDecimal convert(final String o) { - return new BigDecimal(o); - } - }; - } - - /** - * {@link BigInteger} conversion functions. - */ - private abstract static class ToBigInteger extends Converter { - private static final ToBigInteger FROM_STRING = new ToBigInteger() { - @Override - public BigInteger convert(final String o) { - return new BigInteger(o); - } - }; - } - - /** - * {@link Boolean} conversion functions. - */ - private abstract static class ToBoolean extends Converter { - private static final ToBoolean FROM_STRING = new ToBoolean() { - private final Pattern n0 = Pattern.compile("(?i)[N0]"); - private final Pattern y1 = Pattern.compile("(?i)[Y1]"); - - @Override - public Boolean convert(final String o) { - return n0.matcher(o).matches() ? Boolean.FALSE : y1.matcher(o).matches() ? Boolean.TRUE : Boolean.valueOf(o); - } - }; - } - - /** - * {@link Byte} conversion functions. - */ - private abstract static class ToByte extends Converter { - private static final ToByte FROM_NUMBER = new ToByte() { - @Override - public Byte convert(final Number o) { - return o.byteValue(); - } - }; - - private static final ToByte FROM_STRING = new ToByte() { - @Override - public Byte convert(final String o) { - return Byte.valueOf(o); - } - }; - } - - /** - * {@link byte} array conversion functions. - */ - private abstract static class ToByteArray extends Converter { - private static final ToByteArray FROM_BYTE_BUFFER = new ToByteArray() { - @Override - public byte[] convert(final ByteBuffer o) { - if (o.hasArray()) { - return o.array(); - } - final byte[] value = new byte[o.remaining()]; - o.get(value); - return value; - } - }; - - private static final ToByteArray FROM_STRING = new ToByteArray() { - @Override - public byte[] convert(final String o) { - return o.getBytes(Charset.forName("UTF-8")); - } - }; - } - - /** - * {@link ByteBuffer} conversion functions. - */ - private abstract static class ToByteBuffer extends Converter { - private static final ToByteBuffer FROM_BYTE_ARRAY = new ToByteBuffer() { - @Override - public ByteBuffer convert(final byte[] o) { - return ByteBuffer.wrap(o); - } - }; - - private static final ToByteBuffer FROM_UUID = new ToByteBuffer() { - @Override - public ByteBuffer convert(final java.util.UUID o) { - final ByteBuffer value = ByteBuffer.allocate(16); - value.putLong(o.getMostSignificantBits()).putLong(o.getLeastSignificantBits()); - value.position(0); - return value; - } - }; - } - - /** - * {@link Calendar} conversion functions. - */ - private abstract static class ToCalendar extends Converter { - private static final ToCalendar FROM_DATE = new ToCalendar() { - @Override - public Calendar convert(final Date o) { - final Calendar value = Calendar.getInstance(); - value.setTime(o); - return value; - } - }; - private static final ToCalendar FROM_INSTANT = new ToCalendar() { - @Override - public Calendar convert(Instant o) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(o.toEpochMilli()); - return cal; - } - }; - } - - /** - * {@link Character} conversion functions. - */ - private abstract static class ToCharacter extends Converter { - private static final ToCharacter FROM_STRING = new ToCharacter() { - @Override - public Character convert(final String o) { - return Character.valueOf(o.charAt(0)); - } - }; - } - - /** - * {@link Currency} conversion functions. - */ - private abstract static class ToCurrency extends Converter { - private static final ToCurrency FROM_STRING = new ToCurrency() { - @Override - public Currency convert(final String o) { - return Currency.getInstance(o); - } - }; - } - - private abstract static class ToDate extends Converter { - private static final ToDate FROM_INSTANT = new ToDate() { - @Override - public Date convert(Instant o) { - return Date.from(o); - } - }; - private static final ToDate FROM_CALENDAR = new ToDate() { - @Override - public Date convert(Calendar o) { - return o.getTime(); - } - }; - private static final ToDate FROM_STRING = new ToDate() { - @Override - public Date convert(String s) { - return Date.from(DateUtils.parseIso8601Date(s)); - } - }; - } - - /** - * {@link Instant} conversion functions. - */ - private abstract static class ToInstant extends Converter { - private static final ToInstant FROM_CALENDAR = new ToInstant() { - @Override - public Instant convert(final Calendar o) { - return o.toInstant(); - } - }; - - private static final ToInstant FROM_ZONEDDATETIME = new ToInstant() { - @Override - public Instant convert(final ZonedDateTime o) { - return o.toInstant(); - } - }; - - private static final ToInstant FROM_LONG = new ToInstant() { - @Override - public Instant convert(final Long o) { - return Instant.ofEpochMilli(o); - } - }; - - private static final ToInstant FROM_STRING = new ToInstant() { - @Override - public Instant convert(final String o) { - return DateUtils.parseIso8601Date(o); - } - }; - } - - /** - * {@link java.time.ZonedDateTime} conversion functions. - */ - private abstract static class ToDateTime extends Converter { - private static final ToDateTime FROM_DATE = new ToDateTime() { - public ZonedDateTime convert(final Date o) { - return ZonedDateTime.ofInstant(o.toInstant(), ZoneOffset.UTC); - } - }; - private static final ToDateTime FROM_INSTANT_AT_UTC = new ToDateTime() { - public ZonedDateTime convert(final Instant o) { - return ZonedDateTime.ofInstant(o, ZoneOffset.UTC); - } - }; - - private static final ToDateTime FROM_CALENDAR = new ToDateTime() { - @Override - public ZonedDateTime convert(Calendar o) { - return ZonedDateTime.ofInstant(Instant.ofEpochMilli(o.getTimeInMillis()), o.getTimeZone().toZoneId()); - } - }; - } - - /** - * {@link Double} conversion functions. - */ - private abstract static class ToDouble extends Converter { - private static final ToDouble FROM_NUMBER = new ToDouble() { - @Override - public Double convert(final Number o) { - return o.doubleValue(); - } - }; - - private static final ToDouble FROM_STRING = new ToDouble() { - @Override - public Double convert(final String o) { - return Double.valueOf(o); - } - }; - } - - /** - * {@link Enum} from {@link String} - */ - private abstract static class ToEnum, T> extends Converter { - private static final class FromString> extends ToEnum { - private final Class sourceType; - - private FromString(final Class sourceType) { - this.sourceType = sourceType; - } - - @Override - public S convert(final String o) { - return Enum.valueOf(sourceType, o); - } - } - } - - /** - * {@link Float} conversion functions. - */ - private abstract static class ToFloat extends Converter { - private static final ToFloat FROM_NUMBER = new ToFloat() { - @Override - public Float convert(final Number o) { - return o.floatValue(); - } - }; - - private static final ToFloat FROM_STRING = new ToFloat() { - @Override - public Float convert(final String o) { - return Float.valueOf(o); - } - }; - } - - /** - * {@link Integer} conversion functions. - */ - private abstract static class ToInteger extends Converter { - private static final ToInteger FROM_NUMBER = new ToInteger() { - @Override - public Integer convert(final Number o) { - return o.intValue(); - } - }; - - private static final ToInteger FROM_STRING = new ToInteger() { - @Override - public Integer convert(final String o) { - return Integer.valueOf(o); - } - }; - } - - /** - * {@link Locale} conversion functions. - */ - private abstract static class ToLocale extends Converter { - private static final ToLocale FROM_STRING = new ToLocale() { - @Override - public Locale convert(final String o) { - final String[] value = o.split("-", 3); - if (value.length == 3) { - return new Locale(value[0], value[1], value[2]); - } - if (value.length == 2) { - return new Locale(value[0], value[1]); - } - return new Locale(value[0]); //JDK7+: return Locale.forLanguageTag(o); - } - }; - } - - /** - * {@link Long} conversion functions. - */ - private abstract static class ToLong extends Converter { - private static final ToLong FROM_DATE = new ToLong() { - @Override - public Long convert(final Date o) { - return o.getTime(); - } - }; - private static final ToLong FROM_TEMPORAL_ACCESSOR = new ToLong() { - @Override - public Long convert(final Instant o) { - return o.toEpochMilli(); - } - }; - - private static final ToLong FROM_NUMBER = new ToLong() { - @Override - public Long convert(final Number o) { - return o.longValue(); - } - }; - - private static final ToLong FROM_STRING = new ToLong() { - @Override - public Long convert(final String o) { - return Long.valueOf(o); - } - }; - } - - /** - * {@link Short} conversion functions. - */ - private abstract static class ToShort extends Converter { - private static final ToShort FROM_NUMBER = new ToShort() { - @Override - public Short convert(final Number o) { - return o.shortValue(); - } - }; - - private static final ToShort FROM_STRING = new ToShort() { - @Override - public Short convert(final String o) { - return Short.valueOf(o); - } - }; - } - - /** - * {@link String} conversion functions. - */ - private abstract static class ToString extends Converter { - private static final ToString FROM_BOOLEAN = new ToString() { - @Override - public String convert(final Boolean o) { - return Boolean.TRUE.equals(o) ? "1" : "0"; - } - }; - - private static final ToString FROM_BYTE_ARRAY = new ToString() { - @Override - public String convert(final byte[] o) { - return new String(o, Charset.forName("UTF-8")); - } - }; - - private static final ToString FROM_DATE = new ToString() { - @Override - public String convert(final Date o) { - return DateUtils.formatIso8601Date(o.toInstant()); - } - }; - - private static final ToString FROM_ENUM = new ToString() { - @Override - public String convert(final Enum o) { - return o.name(); - } - }; - - private static final ToString FROM_LOCALE = new ToString() { - @Override - public String convert(final Locale o) { - final StringBuilder value = new StringBuilder(o.getLanguage()); - if (!o.getCountry().isEmpty() || !o.getVariant().isEmpty()) { - value.append("-").append(o.getCountry()); - } - if (!o.getVariant().isEmpty()) { - value.append("-").append(o.getVariant()); - } - return value.toString(); //JDK7+: return o.toLanguageTag(); - } - }; - - private static final ToString FROM_NUMBER = new ToString() { - @Override - public String convert(final Number o) { - return o.toString(); - } - }; - - private static final ToString FROM_TIME_ZONE = new ToString() { - @Override - public String convert(final TimeZone o) { - return o.getID(); - } - }; - private static final ToString FROM_ZONE_ID = new ToString() { - @Override - public String convert(final ZoneId o) { - return o.getId(); - } - }; - - private static final ToString FROM_OBJECT = new ToString() { - @Override - public String convert(final Object o) { - return o.toString(); - } - }; - - private static final ToString FROM_DATE_TIME = new ToString() { - @Override - public String convert(ZonedDateTime o) { - return ISO_DATE_TIME.format(o); - } - }; - } - - /** - * {@link TimeZone} conversion functions. - */ - private abstract static class ToTimeZone extends Converter { - private static final ToTimeZone FROM_STRING = new ToTimeZone() { - @Override - public TimeZone convert(final String o) { - return TimeZone.getTimeZone(o); - } - }; - } - - /** - * {@link ZoneId} conversion functions. - */ - private abstract static class ToZoneId extends Converter { - private static final ToZoneId FROM_STRING = new ToZoneId() { - @Override - public ZoneId convert(final String o) { - return ZoneId.of(o); - } - }; - } - - /** - * {@link java.net.URL} conversion functions. - */ - private abstract static class ToUrl extends Converter { - private static final ToUrl FROM_STRING = new ToUrl() { - @Override - public java.net.URL convert(final String o) { - try { - return new java.net.URL(o); - } catch (final java.net.MalformedURLException e) { - throw new IllegalArgumentException("malformed URL", e); - } - } - }; - } - - /** - * {@link java.net.URI} conversion functions. - */ - private abstract static class ToUri extends Converter { - private static final ToUri FROM_STRING = new ToUri() { - @Override - public java.net.URI convert(final String o) { - try { - return new java.net.URI(o); - } catch (final java.net.URISyntaxException e) { - throw new IllegalArgumentException("malformed URI", e); - } - } - }; - } - - /** - * {@link java.util.UUID} conversion functions. - */ - private abstract static class ToUuid extends Converter { - private static final ToUuid FROM_BYTE_BUFFER = new ToUuid() { - @Override - public java.util.UUID convert(final ByteBuffer o) { - return new java.util.UUID(o.getLong(), o.getLong()); - } - }; - - private static final ToUuid FROM_STRING = new ToUuid() { - @Override - public java.util.UUID convert(final String o) { - return java.util.UUID.fromString(o); - } - }; - } - - /** - * {@link Object} conversion functions. - */ - private abstract static class ToObject extends Converter { - private static final ToObject FROM_OBJECT = new ToObject() { - @Override - public Object convert(final Object o) { - return o; - } - }; - } - - /** - * One-way type-converter. - */ - abstract static class Converter { - final Converter join(final Converter target) { - final Converter source = this; - return new Converter() { - @Override - public S convert(final U o) { - return source.convert(target.convert(o)); - } - }; - } - - public abstract S convert(T o); - } - - private static class StandardDynamoDbTypeConverter implements DynamoDbTypeConverter { - private final Converter toSource; - private final Converter toTarget; - - StandardDynamoDbTypeConverter(Converter toSource, Converter toTarget) { - this.toSource = toSource; - this.toTarget = toTarget; - } - - @Override - public final S convert(final T o) { - return toSource.convert(o); - } - - @Override - public final T unconvert(final S o) { - return toTarget.convert(o); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StringListMapTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StringListMapTest.java deleted file mode 100644 index 69d478e13a2e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/StringListMapTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import static org.junit.Assert.assertEquals; - -import java.util.List; -import org.junit.Test; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper.StringListMap; - -/** - * Unit tests for {@link StringListMap}. - */ -public class StringListMapTest { - - private StringListMap map() { - final StringListMap map = new StringListMap(); - for (int i = 1, its = 1; i <= its; i++) { - map.add("A", Integer.valueOf(i)); - } - for (int i = 1, its = 25; i <= its; i++) { - map.add("B", Integer.valueOf(i)); - } - for (int i = 1, its = 10; i <= its; i++) { - map.add("C", Integer.valueOf(i)); - } - for (int i = 1, its = 5; i <= its; i++) { - map.add("D", Integer.valueOf(i)); - } - for (int i = 1, its = 10; i <= its; i++) { - map.add("E", Integer.valueOf(i)); - } - for (int i = 1, its = 25; i <= its; i++) { - map.add("F", Integer.valueOf(i)); - } - for (int i = 1, its = 1; i <= its; i++) { - map.add("G", Integer.valueOf(i)); - } - assertEquals(7, map.size()); - return map; - } - - @Test - public void testSubMaps20PerMap() { - assertSizes(map().subMaps(20, true), new int[][] {{1, 19}, {6, 10, 4}, {1, 10, 9}, {16, 1}}); - } - - @Test - public void testSubMaps11PerMap() { - assertSizes(map().subMaps(11, true), new int[][] {{1, 10}, {11}, {4, 7}, {3, 5, 3}, {7, 4}, {11}, {10, 1}}); - } - - @Test - public void testSubMapsInto4() { - assertSizes(map().subMaps(4, false), new int[][] {{1, 6, 2, 2, 2, 6, 1}, {7, 2, 1, 3, 6}, {6, 3, 1, 3, 6}, {6, 3, 1, 2, 7}}); - } - - private void assertSizes(List> subMaps, int[][] sizes) { - try { - assertEquals(sizes.length, subMaps.size()); - - for (int mapIndex = 0, keyIndex = 0; mapIndex < sizes.length; mapIndex++, keyIndex = 0) { - final StringListMap subMap = subMaps.get(mapIndex); - assertEquals("subMaps[" + mapIndex + "]", sizes[mapIndex].length, subMap.size()); - - for (final List values : subMap.values()) { - assertEquals("subMaps[" + mapIndex + "][" + keyIndex + "]", sizes[mapIndex][keyIndex], values.size()); - keyIndex++; - } - } - } catch (final Error e) { - System.out.println("subMaps=" + subMaps); - throw e; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/TestParameters.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/TestParameters.java deleted file mode 100644 index 0ba70d9924d7..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/TestParameters.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class TestParameters - implements AttributeTransformer.Parameters { - - private Map attributeValues; - - public TestParameters() { - this(null); - } - - public TestParameters(Map attributeValues) { - this.attributeValues = attributeValues; - } - - public Map getAttributeValues() { - return attributeValues; - } - - public boolean isPartialUpdate() { - return false; - } - - public Class modelClass() { - return null; - } - - public DynamoDbMapperConfig mapperConfig() { - return null; - } - - public String getTableName() { - return null; - } - - public String getHashKeyName() { - return null; - } - - public String getRangeKeyName() { - return null; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/UnmarshallerTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/UnmarshallerTest.java deleted file mode 100644 index 3fed28fc67e2..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/UnmarshallerTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class UnmarshallerTest extends StandardModelFactoriesV2UnconvertTest { - - private static final ItemConverter CONVERTER = CONFIG.getConversionSchema().getConverter( - new ConversionSchema.Dependencies().with(S3ClientCache.class, new S3ClientCache((AwsCredentialsProvider) null))); - - @Override - protected Object unconvert(Class clazz, Method getter, Method setter, AttributeValue value) { - return CONVERTER.unconvert(getter, setter, value); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V1MarshallerTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V1MarshallerTest.java deleted file mode 100644 index f3166984e6fd..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V1MarshallerTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class V1MarshallerTest extends StandardModelFactoriesV1Test { - - private static final ItemConverter CONVERTER = CONFIG.getConversionSchema().getConverter( - new ConversionSchema.Dependencies()); - - @Override - protected AttributeValue convert(Class clazz, Method getter, Object value) { - return CONVERTER.convert(getter, value); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V2CompatMarshallerTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V2CompatMarshallerTest.java deleted file mode 100644 index 78ded601a426..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V2CompatMarshallerTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class V2CompatMarshallerTest extends StandardModelFactoriesV2CompatibleTest { - - private static final ItemConverter CONVERTER = CONFIG.getConversionSchema().getConverter( - new ConversionSchema.Dependencies()); - - @Override - protected AttributeValue convert(Class clazz, Method getter, Object value) { - return CONVERTER.convert(getter, value); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V2MarshallerTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V2MarshallerTest.java deleted file mode 100644 index cf879edceb8c..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/V2MarshallerTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class V2MarshallerTest extends StandardModelFactoriesV2Test { - - private static final ItemConverter CONVERTER = CONFIG.getConversionSchema().getConverter( - new ConversionSchema.Dependencies()); - - @Override - protected AttributeValue convert(Class clazz, Method getter, Object value) { - return CONVERTER.convert(getter, value); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanSetToNumberSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanSetToNumberSetMarshaller.java deleted file mode 100644 index beff5cf7f943..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanSetToNumberSetMarshaller.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.NumberSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A legacy marshaller that marshals sets of Java {@code Booleans} into DynamoDB - * NumberSets, representing {@code true} as '1' and {@code false} as '0'. - * Retained for backwards compatibility with older versions of the mapper which - * don't know about the DynamoDB BOOL type. - */ -public class BooleanSetToNumberSetMarshaller - implements NumberSetAttributeMarshaller { - - private static final BooleanSetToNumberSetMarshaller INSTANCE = - new BooleanSetToNumberSetMarshaller(); - - private BooleanSetToNumberSetMarshaller() { - } - - public static BooleanSetToNumberSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set booleans = (Set) obj; - List booleanAttributes = new ArrayList(booleans.size()); - - for (Boolean b : booleans) { - if (b == null || b == false) { - booleanAttributes.add("0"); - } else { - booleanAttributes.add("1"); - } - } - - return AttributeValue.builder().ns(booleanAttributes).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanToBooleanMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanToBooleanMarshaller.java deleted file mode 100644 index 0045ee31fa23..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanToBooleanMarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BooleanAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code Boolean} objects to Dynamodb-native - * {@code BOOL} attribute values. - */ -public class BooleanToBooleanMarshaller implements BooleanAttributeMarshaller { - - private static final BooleanToBooleanMarshaller INSTANCE = - new BooleanToBooleanMarshaller(); - - private BooleanToBooleanMarshaller() { - } - - public static BooleanToBooleanMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - return AttributeValue.builder().bool((Boolean) obj).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanToNumberMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanToNumberMarshaller.java deleted file mode 100644 index c757e3d79a40..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/BooleanToNumberMarshaller.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.NumberAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A legacy marshaller that marshals Java {@code Booleans} into DynamoDB - * Numbers, representing {@code true} as '1' and {@code false} as '0'. Retained - * for backwards compatibility with older versions of the mapper which don't - * know about the DynamoDB BOOL type. - */ -public class BooleanToNumberMarshaller implements NumberAttributeMarshaller { - - private static final BooleanToNumberMarshaller INSTANCE = - new BooleanToNumberMarshaller(); - - private BooleanToNumberMarshaller() { - } - - public static BooleanToNumberMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - Boolean bool = (Boolean) obj; - if (bool == null || bool == false) { - return AttributeValue.builder().n("0").build(); - } else { - return AttributeValue.builder().n("1").build(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteArraySetToBinarySetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteArraySetToBinarySetMarshaller.java deleted file mode 100644 index 4b4db1f7fd23..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteArraySetToBinarySetMarshaller.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BinarySetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code byte[]}s into DynamoDB - * BinarySet attributes. - */ -public class ByteArraySetToBinarySetMarshaller - implements BinarySetAttributeMarshaller { - - private static final ByteArraySetToBinarySetMarshaller INSTANCE = - new ByteArraySetToBinarySetMarshaller(); - - private ByteArraySetToBinarySetMarshaller() { - } - - public static ByteArraySetToBinarySetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set buffers = (Set) obj; - List attributes = new ArrayList<>(buffers.size()); - - for (byte[] b : buffers) { - attributes.add(SdkBytes.fromByteArray(b)); - } - - return AttributeValue.builder().bs(attributes).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteArrayToBinaryMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteArrayToBinaryMarshaller.java deleted file mode 100644 index f188d8a486af..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteArrayToBinaryMarshaller.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BinaryAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code byte[]}s into DynamoDB Binary - * attributes. - */ -public class ByteArrayToBinaryMarshaller implements BinaryAttributeMarshaller { - - private static final ByteArrayToBinaryMarshaller INSTANCE = - new ByteArrayToBinaryMarshaller(); - - private ByteArrayToBinaryMarshaller() { - } - - public static ByteArrayToBinaryMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - return AttributeValue.builder().b(SdkBytes.fromByteArray((byte[]) obj)).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteBufferSetToBinarySetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteBufferSetToBinarySetMarshaller.java deleted file mode 100644 index f305ac86a507..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteBufferSetToBinarySetMarshaller.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BinarySetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code ByteBuffer}s into DynamoDB - * BinarySet attributes. - */ -public class ByteBufferSetToBinarySetMarshaller - implements BinarySetAttributeMarshaller { - - private static final ByteBufferSetToBinarySetMarshaller INSTANCE = - new ByteBufferSetToBinarySetMarshaller(); - - private ByteBufferSetToBinarySetMarshaller() { - } - - public static ByteBufferSetToBinarySetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set buffers = (Set) obj; - List attributes = new ArrayList(buffers.size()); - - for (ByteBuffer b : buffers) { - attributes.add(SdkBytes.fromByteBuffer(b)); - } - - return AttributeValue.builder().bs(attributes).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteBufferToBinaryMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteBufferToBinaryMarshaller.java deleted file mode 100644 index 2f9d7fcd3aa6..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ByteBufferToBinaryMarshaller.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.nio.ByteBuffer; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.BinaryAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code ByteBuffer}s into DynamoDB Binary - * attributes. - */ -public class ByteBufferToBinaryMarshaller implements BinaryAttributeMarshaller { - - private static final ByteBufferToBinaryMarshaller INSTANCE = - new ByteBufferToBinaryMarshaller(); - - private ByteBufferToBinaryMarshaller() { - } - - public static ByteBufferToBinaryMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - return AttributeValue.builder().b(SdkBytes.fromByteBuffer((ByteBuffer) obj)).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CalendarSetToStringSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CalendarSetToStringSetMarshaller.java deleted file mode 100644 index ad5b1fa63330..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CalendarSetToStringSetMarshaller.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import static software.amazon.awssdk.utils.DateUtils.formatIso8601Date; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code Calendar} objects into - * DynamoDB StringSets (in ISO 8601 format, ie {"2014-01-01T00:00:00Z", ...}). - */ -public class CalendarSetToStringSetMarshaller - implements StringSetAttributeMarshaller { - - private static final CalendarSetToStringSetMarshaller INSTANCE = - new CalendarSetToStringSetMarshaller(); - - private CalendarSetToStringSetMarshaller() { - } - - public static CalendarSetToStringSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set dates = (Set) obj; - - List timestamps = new ArrayList(dates.size()); - for (Calendar calendar : dates) { - timestamps.add(formatIso8601Date(calendar.toInstant())); - } - - return AttributeValue.builder().ss(timestamps).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CalendarToStringMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CalendarToStringMarshaller.java deleted file mode 100644 index 1c734f99b29e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CalendarToStringMarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import static software.amazon.awssdk.utils.DateUtils.formatIso8601Date; - -import java.util.Calendar; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code Calendar} objects into DynamoDB - * Strings (in ISO 8601 format, ie "2014-01-01T00:00:00Z"). - */ -public class CalendarToStringMarshaller implements StringAttributeMarshaller { - - private static final CalendarToStringMarshaller INSTANCE = - new CalendarToStringMarshaller(); - - private CalendarToStringMarshaller() { - } - - public static CalendarToStringMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - Calendar calendar = (Calendar) obj; - return AttributeValue.builder().s( - formatIso8601Date(calendar.toInstant())).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CollectionToListMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CollectionToListMarshaller.java deleted file mode 100644 index 7135d06ef751..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CollectionToListMarshaller.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.ListAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class CollectionToListMarshaller implements ListAttributeMarshaller { - - private static final CollectionToListMarshaller INSTANCE = - new CollectionToListMarshaller(); - private final ArgumentMarshaller memberMarshaller; - - - private CollectionToListMarshaller() { - this(null); - } - - public CollectionToListMarshaller(ArgumentMarshaller memberMarshaller) { - this.memberMarshaller = memberMarshaller; - } - - public static CollectionToListMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - if (memberMarshaller == null) { - throw new IllegalStateException( - "No member marshaller configured!"); - } - - Collection objects = (Collection) obj; - List values = - new ArrayList(objects.size()); - - for (Object o : objects) { - AttributeValue value; - if (o == null) { - value = AttributeValue.builder().nul(true).build(); - } else { - value = memberMarshaller.marshall(o); - } - - values.add(value); - } - - AttributeValue result = AttributeValue.builder().l(values).build(); - return result; - } - - public ArgumentMarshaller memberMarshaller() { - return memberMarshaller; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CustomMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CustomMarshaller.java deleted file mode 100644 index 61cf9f93bb45..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/CustomMarshaller.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that delegates to an instance of a - * {@code DynamoDBMarshalling}-derived custom marshaler. - */ -public class CustomMarshaller implements StringAttributeMarshaller { - - private final Class> marshallerClass; - - public CustomMarshaller( - Class> marshallerClass) { - - this.marshallerClass = marshallerClass; - } - - @SuppressWarnings("unchecked") - private static DynamoDbMarshaller createMarshaller(Class clazz) { - try { - - return (DynamoDbMarshaller) clazz.newInstance(); - - } catch (InstantiationException e) { - throw new DynamoDbMappingException( - "Failed to instantiate custom marshaler for class " + clazz, - e); - - } catch (IllegalAccessException e) { - throw new DynamoDbMappingException( - "Failed to instantiate custom marshaler for class " + clazz, - e); - } - } - - @Override - public AttributeValue marshall(Object obj) { - - // TODO: Would be nice to cache this object, but not sure if we can - // do that now without a breaking change; user's marshalers might - // not all be thread-safe. - - DynamoDbMarshaller marshaler = - createMarshaller(marshallerClass); - - String stringValue = marshaler.marshall(obj); - - if (stringValue == null) { - return null; - } else { - return AttributeValue.builder().s(stringValue).build(); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/DateSetToStringSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/DateSetToStringSetMarshaller.java deleted file mode 100644 index 51e942c16bb9..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/DateSetToStringSetMarshaller.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.utils.DateUtils; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code Date} objects into DynamoDB - * StringSets (in ISO 8601 format, ie {"2014-01-01T00:00:00Z", ...}). - */ -public class DateSetToStringSetMarshaller - implements StringSetAttributeMarshaller { - - private static final DateSetToStringSetMarshaller INSTANCE = - new DateSetToStringSetMarshaller(); - - private DateSetToStringSetMarshaller() { - } - - public static DateSetToStringSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set dates = (Set) obj; - - List timestamps = new ArrayList(dates.size()); - for (Date date : dates) { - timestamps.add(DateUtils.formatIso8601Date(date.toInstant())); - } - - return AttributeValue.builder().ss(timestamps).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/DateToStringMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/DateToStringMarshaller.java deleted file mode 100644 index 304bd6e606e6..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/DateToStringMarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import static software.amazon.awssdk.utils.DateUtils.formatIso8601Date; - -import java.util.Date; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code Date} objects into DynamoDB Strings - * (in ISO 8601 format, ie "2014-01-01T00:00:00Z"). - */ -public class DateToStringMarshaller implements StringAttributeMarshaller { - - private static final DateToStringMarshaller INSTANCE = - new DateToStringMarshaller(); - - private DateToStringMarshaller() { - } - - public static DateToStringMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - return AttributeValue.builder().s( - formatIso8601Date(Date.class.cast(obj).toInstant())) - .build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/MapToMapMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/MapToMapMarshaller.java deleted file mode 100644 index 4a35b2a9c15d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/MapToMapMarshaller.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.MapAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class MapToMapMarshaller implements MapAttributeMarshaller { - - private static final MapToMapMarshaller INSTANCE = - new MapToMapMarshaller(); - private final ArgumentMarshaller memberMarshaller; - - - private MapToMapMarshaller() { - memberMarshaller = null; - } - - public MapToMapMarshaller(ArgumentMarshaller memberMarshaller) { - if (memberMarshaller == null) { - throw new NullPointerException("memberMarshaller"); - } - this.memberMarshaller = memberMarshaller; - } - - public static MapToMapMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - if (memberMarshaller == null) { - throw new IllegalStateException( - "No member marshaller configured!"); - } - - @SuppressWarnings("unchecked") - Map map = (Map) obj; - Map values = - new HashMap(); - - for (Map.Entry entry : map.entrySet()) { - AttributeValue value; - if (entry.getValue() == null) { - value = AttributeValue.builder().nul(true).build(); - } else { - value = memberMarshaller.marshall(entry.getValue()); - } - - values.put(entry.getKey(), value); - } - - AttributeValue result = AttributeValue.builder().m(values).build(); - //result.setM(values); - return result; - } - - public ArgumentMarshaller memberMarshaller() { - return memberMarshaller; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/NumberSetToNumberSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/NumberSetToNumberSetMarshaller.java deleted file mode 100644 index ccf4b4ced273..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/NumberSetToNumberSetMarshaller.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.NumberSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code Number}s into DynamoDB - * NumberSets. - */ -public class NumberSetToNumberSetMarshaller - implements NumberSetAttributeMarshaller { - - private static final NumberSetToNumberSetMarshaller INSTANCE = - new NumberSetToNumberSetMarshaller(); - - private NumberSetToNumberSetMarshaller() { - } - - public static NumberSetToNumberSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set numbers = (Set) obj; - List numberAttributes = new ArrayList(numbers.size()); - - for (Number n : numbers) { - numberAttributes.add(n.toString()); - } - - return AttributeValue.builder().ns(numberAttributes).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/NumberToNumberMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/NumberToNumberMarshaller.java deleted file mode 100644 index c23322942a57..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/NumberToNumberMarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.NumberAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals any Java {@code Number} to a DynamoDB number. - */ -public class NumberToNumberMarshaller implements NumberAttributeMarshaller { - - private static final NumberToNumberMarshaller INSTANCE = - new NumberToNumberMarshaller(); - - private NumberToNumberMarshaller() { - } - - public static NumberToNumberMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - Number number = (Number) obj; - return AttributeValue.builder().n(number.toString()).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectSetToStringSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectSetToStringSetMarshaller.java deleted file mode 100644 index 9d028316dced..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectSetToStringSetMarshaller.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A legacy marshaller that marshals sets of arbitrary Java objects into - * DynamoDB StringSets by using {@link String#valueOf(Object)}. Retained for - * backwards compatibility in case someone is relying on this, but logs a - * warning if ever used since we only know how to unmarshal back to Java - * Strings. - */ -public class ObjectSetToStringSetMarshaller - implements StringSetAttributeMarshaller { - - private static final Logger log = - LoggerFactory.getLogger(ObjectSetToStringSetMarshaller.class); - - private static final ObjectSetToStringSetMarshaller INSTANCE = - new ObjectSetToStringSetMarshaller(); - - private ObjectSetToStringSetMarshaller() { - } - - public static ObjectSetToStringSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - Set set = (Set) obj; - - log.warn("Marshaling a set of non-String objects to a DynamoDB " - + "StringSet. You won't be able to read these objects back " - + "out of DynamoDB unless you REALLY know what you're doing: " - + "it's probably a bug. If you DO know what you're doing feel" - + "free to ignore this warning, but consider using a custom " - + "marshaler for this instead."); - - List strings = new ArrayList(set.size()); - for (Object o : set) { - strings.add(String.valueOf(o)); - } - - return AttributeValue.builder().ss(strings).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectToMapMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectToMapMarshaller.java deleted file mode 100644 index 2075a16d00d7..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectToMapMarshaller.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.MapAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.ItemConverter; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class ObjectToMapMarshaller implements MapAttributeMarshaller { - - private static final ObjectToMapMarshaller INSTANCE = - new ObjectToMapMarshaller(); - private final ItemConverter converter; - - private ObjectToMapMarshaller() { - converter = null; - } - - public ObjectToMapMarshaller(ItemConverter converter) { - if (converter == null) { - throw new NullPointerException("converter"); - } - this.converter = converter; - } - - public static ObjectToMapMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - Map values = converter.convert(obj); - return AttributeValue.builder().m(values).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectToStringMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectToStringMarshaller.java deleted file mode 100644 index 16869104f830..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/ObjectToStringMarshaller.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code Object} objects into DynamoDB - * Strings. - * - * @author Sergei Egorov - */ -public class ObjectToStringMarshaller implements StringAttributeMarshaller { - - private static final ObjectToStringMarshaller INSTANCE = - new ObjectToStringMarshaller(); - - private ObjectToStringMarshaller() { - } - - public static ObjectToStringMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - return AttributeValue.builder().s(obj.toString()).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/S3LinkToStringMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/S3LinkToStringMarshaller.java deleted file mode 100644 index e91d640e9042..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/S3LinkToStringMarshaller.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.S3Link; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals {@code S3Link} objects to DynamoDB Strings, - * using a JSON encoding. For example: {"s3":{"region":"us-west-2", - * "bucket":"my-bucket-name", "key": "foo/bar/baz.txt"}}. - */ -public class S3LinkToStringMarshaller implements StringAttributeMarshaller { - - private static final S3LinkToStringMarshaller INSTANCE = - new S3LinkToStringMarshaller(); - - private S3LinkToStringMarshaller() { - } - - public static S3LinkToStringMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - S3Link s3link = (S3Link) obj; - - if (s3link.bucketName() == null || s3link.getKey() == null) { - // insufficient S3 resource specification - return null; - } - - return AttributeValue.builder().s(s3link.toJson()).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/StringSetToStringSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/StringSetToStringSetMarshaller.java deleted file mode 100644 index aae6155c4e6f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/StringSetToStringSetMarshaller.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code String}s to DynamoDB - * StringSets. - */ -public class StringSetToStringSetMarshaller - implements StringSetAttributeMarshaller { - - private static final StringSetToStringSetMarshaller INSTANCE = - new StringSetToStringSetMarshaller(); - - private StringSetToStringSetMarshaller() { - } - - public static StringSetToStringSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set set = (Set) obj; - - List strings = new ArrayList(set.size()); - for (String s : set) { - strings.add(s); - } - - return AttributeValue.builder().ss(strings).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/StringToStringMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/StringToStringMarshaller.java deleted file mode 100644 index abd03f07182f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/StringToStringMarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals Java {@code String} objects to DynamoDB Strings. - */ -public class StringToStringMarshaller implements StringAttributeMarshaller { - - private static final StringToStringMarshaller INSTANCE = - new StringToStringMarshaller(); - - private StringToStringMarshaller() { - } - - public static StringToStringMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - String string = (String) obj; - if (string.length() == 0) { - // Sticking with the legacy behavior for now. - return null; - } - - return AttributeValue.builder().s(string).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/UuidSetToStringSetMarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/UuidSetToStringSetMarshaller.java deleted file mode 100644 index cae6047ff8ac..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/UuidSetToStringSetMarshaller.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentMarshaller.StringSetAttributeMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A marshaller that marshals sets of Java {@code Object} objects into - * DynamoDB StringSets. - */ -public class UuidSetToStringSetMarshaller - implements StringSetAttributeMarshaller { - - private static final UuidSetToStringSetMarshaller INSTANCE = - new UuidSetToStringSetMarshaller(); - - private UuidSetToStringSetMarshaller() { - } - - public static UuidSetToStringSetMarshaller instance() { - return INSTANCE; - } - - @Override - public AttributeValue marshall(Object obj) { - @SuppressWarnings("unchecked") - Set uuids = (Set) obj; - - List strings = new ArrayList(uuids.size()); - for (UUID uuid : uuids) { - strings.add(uuid.toString()); - } - - return AttributeValue.builder().ss(strings).build(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/package-info.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/package-info.java deleted file mode 100644 index be3929bcef17..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/marshallers/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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. - */ - -/** - * Standard implementations of {@code ArgumentMarshaller}. - */ - -package software.amazon.awssdk.services.dynamodb.datamodeling.marshallers; diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BUnmarshaller.java deleted file mode 100644 index 535403cb2dc5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class BUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.b() == null) { - throw new DynamoDbMappingException("Expected B in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigDecimalSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigDecimalSetUnmarshaller.java deleted file mode 100644 index a4300cfe3dbe..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigDecimalSetUnmarshaller.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.math.BigDecimal; -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code BigDecimal}s. - */ -public class BigDecimalSetUnmarshaller extends NsUnmarshaller { - - private static final BigDecimalSetUnmarshaller INSTANCE = - new BigDecimalSetUnmarshaller(); - - private BigDecimalSetUnmarshaller() { - } - - public static BigDecimalSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(new BigDecimal(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigDecimalUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigDecimalUnmarshaller.java deleted file mode 100644 index 2ff631e45575..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigDecimalUnmarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.math.BigDecimal; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code BigDecimal}s. - */ -public class BigDecimalUnmarshaller extends NUnmarshaller { - - private static final BigDecimalUnmarshaller INSTANCE = - new BigDecimalUnmarshaller(); - - private BigDecimalUnmarshaller() { - } - - public static BigDecimalUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return new BigDecimal(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigIntegerSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigIntegerSetUnmarshaller.java deleted file mode 100644 index d008cbb327de..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigIntegerSetUnmarshaller.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.math.BigInteger; -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code BigInteger}s. - */ -public class BigIntegerSetUnmarshaller extends NsUnmarshaller { - - private static final BigIntegerSetUnmarshaller INSTANCE = - new BigIntegerSetUnmarshaller(); - - private BigIntegerSetUnmarshaller() { - } - - public static BigIntegerSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(new BigInteger(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigIntegerUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigIntegerUnmarshaller.java deleted file mode 100644 index 3ec56b6cc1b0..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BigIntegerUnmarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.math.BigInteger; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code BigInteger}s. - */ -public class BigIntegerUnmarshaller extends NUnmarshaller { - - private static final BigIntegerUnmarshaller INSTANCE = - new BigIntegerUnmarshaller(); - - private BigIntegerUnmarshaller() { - } - - public static BigIntegerUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return new BigInteger(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BooleanSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BooleanSetUnmarshaller.java deleted file mode 100644 index 8a0577601bb0..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BooleanSetUnmarshaller.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * A special unmarshaller for Set<Boolean>, which the V1 schema stores as - * an NS using 0/1 for true/false. In the V2 schema these fall through to - * the {@code ObjectSetToListMarshaller} which stores them as an L or BOOLs. - */ -public class BooleanSetUnmarshaller implements ArgumentUnmarshaller { - - private static final BooleanSetUnmarshaller INSTANCE = - new BooleanSetUnmarshaller(); - - private BooleanSetUnmarshaller() { - } - - public static BooleanSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.ns() == null && value.l() == null) { - throw new DynamoDbMappingException( - "Expected either L or NS in value " + value - + " when invoking " + setter); - } - } - - @Override - public Object unmarshall(AttributeValue value) { - if (value.l() != null) { - return unmarshallList(value.l()); - } else { - return unmarshallNs(value.ns()); - } - } - - private Set unmarshallList(List values) { - Set result = new HashSet(); - - for (AttributeValue value : values) { - Boolean bool; - if (Boolean.TRUE.equals(value.nul())) { - bool = null; - } else { - bool = value.bool(); - if (bool == null) { - throw new DynamoDbMappingException( - value + " is not a boolean"); - } - } - - if (!result.add(bool)) { - throw new DynamoDbMappingException( - "Duplicate value (" + bool + ") found in " - + values); - } - } - - return result; - } - - private Set unmarshallNs(List values) { - Set result = new HashSet(); - - for (String s : values) { - if ("1".equals(s)) { - result.add(Boolean.TRUE); - } else if ("0".equals(s)) { - result.add(Boolean.FALSE); - } else { - throw new IllegalArgumentException( - "Expected '1' or '0' for boolean value, was " + s); - } - } - - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BooleanUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BooleanUnmarshaller.java deleted file mode 100644 index 35a0cc49b702..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BooleanUnmarshaller.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Bools (or Numbers) into Java - * {@code Boolean}s. Numbers are handled for backwards compatibility with - * versions of the mapper written before the DynamoDB native Boolean type - * was added, which stored Java {@code Boolean}s as either the Number 0 (false) - * or 1 (true). - */ -public class BooleanUnmarshaller implements ArgumentUnmarshaller { - - private static final BooleanUnmarshaller INSTANCE = - new BooleanUnmarshaller(); - - private BooleanUnmarshaller() { - } - - public static BooleanUnmarshaller instance() { - return INSTANCE; - } - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.n() == null && value.bool() == null) { - throw new DynamoDbMappingException( - "Expected either N or BOOL in value " + value - + " when invoking " + setter); - } - } - - @Override - public Object unmarshall(AttributeValue value) { - if (value.bool() != null) { - return value.bool(); - } - if ("1".equals(value.n())) { - return Boolean.TRUE; - } - if ("0".equals(value.n())) { - return Boolean.FALSE; - } - - throw new IllegalArgumentException( - "Expected '1', '0', or BOOL value for boolean value, was " - + value); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BsUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BsUnmarshaller.java deleted file mode 100644 index 9529d8857679..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/BsUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class BsUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.bs() == null) { - throw new DynamoDbMappingException("Expected BS in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteArraySetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteArraySetUnmarshaller.java deleted file mode 100644 index 129136773ed9..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteArraySetUnmarshaller.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.nio.ByteBuffer; -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals BinarySet values as sets of Java - * {@code byte[]}s. - */ -public class ByteArraySetUnmarshaller extends BsUnmarshaller { - - private static final ByteArraySetUnmarshaller INSTANCE = - new ByteArraySetUnmarshaller(); - - private ByteArraySetUnmarshaller() { - } - - public static ByteArraySetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - - for (SdkBytes bytesBuffer : value.bs()) { - ByteBuffer buffer = bytesBuffer.asByteBuffer(); - if (buffer.hasArray()) { - result.add(buffer.array()); - } else { - byte[] array = new byte[buffer.remaining()]; - buffer.get(array); - result.add(array); - } - } - - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteArrayUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteArrayUnmarshaller.java deleted file mode 100644 index 14454a43beee..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteArrayUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.nio.ByteBuffer; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals Binary values as Java {@code byte[]}s. - */ -public class ByteArrayUnmarshaller extends BUnmarshaller { - - private static final ByteArrayUnmarshaller INSTANCE = - new ByteArrayUnmarshaller(); - - private ByteArrayUnmarshaller() { - } - - public static ByteArrayUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return value.b().asByteArray(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteBufferSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteBufferSetUnmarshaller.java deleted file mode 100644 index 0b85b70edde2..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteBufferSetUnmarshaller.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.stream.Collectors; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals BinarySet values as sets of Java - * {@code ByteBuffer}s. - */ -public class ByteBufferSetUnmarshaller extends BsUnmarshaller { - - private static final ByteBufferSetUnmarshaller INSTANCE = - new ByteBufferSetUnmarshaller(); - - private ByteBufferSetUnmarshaller() { - } - - public static ByteBufferSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return value.bs().stream() - .map(SdkBytes::asByteBuffer) - .collect(Collectors.toSet()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteBufferUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteBufferUnmarshaller.java deleted file mode 100644 index cc5b986226ed..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteBufferUnmarshaller.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals Binary values as Java {@code ByteBuffer}s. - */ -public class ByteBufferUnmarshaller extends BUnmarshaller { - - private static final ByteBufferUnmarshaller INSTANCE = - new ByteBufferUnmarshaller(); - - private ByteBufferUnmarshaller() { - } - - public static ByteBufferUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return value.b().asByteBuffer(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteSetUnmarshaller.java deleted file mode 100644 index 1e29eaeaac0f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteSetUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code Byte}s. - */ -public class ByteSetUnmarshaller extends NsUnmarshaller { - - private static final ByteSetUnmarshaller INSTANCE = - new ByteSetUnmarshaller(); - - private ByteSetUnmarshaller() { - } - - public static ByteSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(Byte.valueOf(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteUnmarshaller.java deleted file mode 100644 index 55773a6459f4..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ByteUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code Byte}s. - */ -public class ByteUnmarshaller extends NUnmarshaller { - - private static final ByteUnmarshaller INSTANCE = - new ByteUnmarshaller(); - - private ByteUnmarshaller() { - } - - public static ByteUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Byte.valueOf(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CalendarSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CalendarSetUnmarshaller.java deleted file mode 100644 index b1c9da0a10ec..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CalendarSetUnmarshaller.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.sql.Date; -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.utils.DateUtils; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals sets of ISO-8601-formatted dates as sets of - * Java {@code Calendar} objects. - */ -public class CalendarSetUnmarshaller extends SsUnmarshaller { - - private static final CalendarSetUnmarshaller INSTANCE = - new CalendarSetUnmarshaller(); - - private CalendarSetUnmarshaller() { - } - - public static CalendarSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - - for (String s : value.ss()) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(Date.from(DateUtils.parseIso8601Date(s))); - result.add(cal); - } - - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CalendarUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CalendarUnmarshaller.java deleted file mode 100644 index 95d3bbe211a2..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CalendarUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import software.amazon.awssdk.utils.DateUtils; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals ISO-8601-formatted dates as Java - * {@code Calendar} objects. - */ -public class CalendarUnmarshaller extends SUnmarshaller { - - private static final CalendarUnmarshaller INSTANCE = - new CalendarUnmarshaller(); - - private CalendarUnmarshaller() { - } - - public static CalendarUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Calendar cal = GregorianCalendar.getInstance(); - cal.setTime(Date.from(DateUtils.parseIso8601Date(value.s()))); - return cal; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CustomUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CustomUnmarshaller.java deleted file mode 100644 index 6ed62268bacc..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/CustomUnmarshaller.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that delegates to an instance of a - * {@code DynamoDBMarshaller}-derived custom marshaler. - */ -public class CustomUnmarshaller extends SUnmarshaller { - - private final Class targetClass; - private final Class> unmarshallerClass; - - public CustomUnmarshaller( - Class targetClass, - Class> unmarshallerClass) { - - this.targetClass = targetClass; - this.unmarshallerClass = unmarshallerClass; - } - - @SuppressWarnings({"rawtypes"}) - private static DynamoDbMarshaller createUnmarshaller(Class clazz) { - try { - - return (DynamoDbMarshaller) clazz.newInstance(); - - } catch (InstantiationException e) { - throw new DynamoDbMappingException( - "Failed to instantiate custom marshaler for class " + clazz, - e); - - } catch (IllegalAccessException e) { - throw new DynamoDbMappingException( - "Failed to instantiate custom marshaler for class " + clazz, - e); - } - } - - @Override - @SuppressWarnings({"rawtypes", "unchecked"}) - public Object unmarshall(AttributeValue value) { - - // TODO: Would be nice to cache this object, but not sure if we can - // do that now without a breaking change; user's unmarshallers - // might not all be thread-safe. - - DynamoDbMarshaller unmarshaller = - createUnmarshaller(unmarshallerClass); - - return unmarshaller.unmarshall(targetClass, value.s()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DateSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DateSetUnmarshaller.java deleted file mode 100644 index defa461a1444..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DateSetUnmarshaller.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.Date; -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.utils.DateUtils; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals sets of ISO-8601-formatted dates as sets of - * Java {@code Date} objects. - */ -public class DateSetUnmarshaller extends SsUnmarshaller { - - private static final DateSetUnmarshaller INSTANCE = - new DateSetUnmarshaller(); - - private DateSetUnmarshaller() { - } - - public static DateSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - - for (String s : value.ss()) { - result.add(Date.from(DateUtils.parseIso8601Date(s))); - } - - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DateUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DateUnmarshaller.java deleted file mode 100644 index da650b7a5906..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DateUnmarshaller.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.Date; -import software.amazon.awssdk.utils.DateUtils; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals ISO-8601-formatted dates as Java - * {@code Date} objects. - */ -public class DateUnmarshaller extends SUnmarshaller { - - private static final DateUnmarshaller INSTANCE = - new DateUnmarshaller(); - - private DateUnmarshaller() { - } - - public static DateUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Date.from(DateUtils.parseIso8601Date(value.s())); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DoubleSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DoubleSetUnmarshaller.java deleted file mode 100644 index 77397c9db8de..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DoubleSetUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code Double}s. - */ -public class DoubleSetUnmarshaller extends NsUnmarshaller { - - private static final DoubleSetUnmarshaller INSTANCE = - new DoubleSetUnmarshaller(); - - private DoubleSetUnmarshaller() { - } - - public static DoubleSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(Double.valueOf(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DoubleUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DoubleUnmarshaller.java deleted file mode 100644 index 9527458c812c..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/DoubleUnmarshaller.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java {@code Double}s. - */ -public class DoubleUnmarshaller extends NUnmarshaller { - - private static final DoubleUnmarshaller INSTANCE = - new DoubleUnmarshaller(); - - private DoubleUnmarshaller() { - } - - public static DoubleUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Double.valueOf(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/FloatSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/FloatSetUnmarshaller.java deleted file mode 100644 index 65a21f6d502b..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/FloatSetUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code Float}s. - */ -public class FloatSetUnmarshaller extends NsUnmarshaller { - - private static final FloatSetUnmarshaller INSTANCE = - new FloatSetUnmarshaller(); - - private FloatSetUnmarshaller() { - } - - public static FloatSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(Float.valueOf(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/FloatUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/FloatUnmarshaller.java deleted file mode 100644 index f38466798a2f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/FloatUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code Float}s. - */ -public class FloatUnmarshaller extends NUnmarshaller { - - private static final FloatUnmarshaller INSTANCE = - new FloatUnmarshaller(); - - private FloatUnmarshaller() { - } - - public static FloatUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Float.valueOf(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/IntegerSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/IntegerSetUnmarshaller.java deleted file mode 100644 index 5a6a25e70109..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/IntegerSetUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code Integer}s. - */ -public class IntegerSetUnmarshaller extends NsUnmarshaller { - - private static final IntegerSetUnmarshaller INSTANCE = - new IntegerSetUnmarshaller(); - - private IntegerSetUnmarshaller() { - } - - public static IntegerSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(Integer.valueOf(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/IntegerUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/IntegerUnmarshaller.java deleted file mode 100644 index 1a873aefdc3f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/IntegerUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code Integer}s. - */ -public class IntegerUnmarshaller extends NUnmarshaller { - - private static final IntegerUnmarshaller INSTANCE = - new IntegerUnmarshaller(); - - private IntegerUnmarshaller() { - } - - public static IntegerUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Integer.valueOf(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LUnmarshaller.java deleted file mode 100644 index 302610c8db3f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class LUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.l() == null) { - throw new DynamoDbMappingException("Expected L in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ListUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ListUnmarshaller.java deleted file mode 100644 index 49d490061b5a..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ListUnmarshaller.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.List; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals Lists into Java {@code Lists}. - */ -public class ListUnmarshaller extends LUnmarshaller { - - private static final ListUnmarshaller INSTANCE = new ListUnmarshaller(); - private final ArgumentUnmarshaller memberUnmarshaller; - - private ListUnmarshaller() { - memberUnmarshaller = null; - } - - public ListUnmarshaller(ArgumentUnmarshaller memberUnmarshaller) { - if (memberUnmarshaller == null) { - throw new NullPointerException("memberUnmarshaller"); - } - this.memberUnmarshaller = memberUnmarshaller; - } - - public static ListUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) throws ParseException { - List values = value.l(); - List objects = new ArrayList(values.size()); - - for (AttributeValue v : values) { - memberUnmarshaller.typeCheck(v, null); - objects.add(memberUnmarshaller.unmarshall(v)); - } - - return objects; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LongSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LongSetUnmarshaller.java deleted file mode 100644 index fd3baa590c56..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LongSetUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code Long}s. - */ -public class LongSetUnmarshaller extends NsUnmarshaller { - - private static final LongSetUnmarshaller INSTANCE = - new LongSetUnmarshaller(); - - private LongSetUnmarshaller() { - } - - public static LongSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(Long.valueOf(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LongUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LongUnmarshaller.java deleted file mode 100644 index 4c0a6923e5f2..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/LongUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code Long}s. - */ -public class LongUnmarshaller extends NUnmarshaller { - - private static final LongUnmarshaller INSTANCE = - new LongUnmarshaller(); - - private LongUnmarshaller() { - } - - public static LongUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Long.valueOf(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/MUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/MUnmarshaller.java deleted file mode 100644 index a6901461a0c5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/MUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class MUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.m() == null) { - throw new DynamoDbMappingException("Expected M in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/MapUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/MapUnmarshaller.java deleted file mode 100644 index 7aaddf400e4a..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/MapUnmarshaller.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.text.ParseException; -import java.util.HashMap; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class MapUnmarshaller extends MUnmarshaller { - - private static final MapUnmarshaller INSTANCE = new MapUnmarshaller(); - private final ArgumentUnmarshaller memberUnmarshaller; - - private MapUnmarshaller() { - memberUnmarshaller = null; - } - - public MapUnmarshaller(ArgumentUnmarshaller memberUnmarshaller) { - if (memberUnmarshaller == null) { - throw new NullPointerException("memberUnmarshaller"); - } - this.memberUnmarshaller = memberUnmarshaller; - } - - public static MapUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) throws ParseException { - Map map = value.m(); - Map result = new HashMap(); - - for (Map.Entry entry : map.entrySet()) { - memberUnmarshaller.typeCheck(entry.getValue(), null); - result.put(entry.getKey(), - memberUnmarshaller.unmarshall(entry.getValue())); - } - - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NUnmarshaller.java deleted file mode 100644 index f4a624922ac3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class NUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.n() == null) { - throw new DynamoDbMappingException("Expected N in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NsUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NsUnmarshaller.java deleted file mode 100644 index 5940f8eb3670..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NsUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class NsUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.ns() == null) { - throw new DynamoDbMappingException("Expected NS in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NullableUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NullableUnmarshaller.java deleted file mode 100644 index 897680ab882f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/NullableUnmarshaller.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import java.text.ParseException; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class NullableUnmarshaller implements ArgumentUnmarshaller { - - private final ArgumentUnmarshaller wrapped; - - public NullableUnmarshaller(ArgumentUnmarshaller wrapped) { - if (wrapped == null) { - throw new NullPointerException("wrapped"); - } - this.wrapped = wrapped; - } - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.nul() == null) { - wrapped.typeCheck(value, setter); - } - } - - @Override - public Object unmarshall(AttributeValue value) throws ParseException { - if (value.nul() != null) { - return null; - } - return wrapped.unmarshall(value); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ObjectSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ObjectSetUnmarshaller.java deleted file mode 100644 index 91627e6979ce..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ObjectSetUnmarshaller.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.text.ParseException; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class ObjectSetUnmarshaller extends LUnmarshaller { - - private static final ObjectSetUnmarshaller INSTANCE = - new ObjectSetUnmarshaller(); - private final ArgumentUnmarshaller memberUnmarshaller; - - private ObjectSetUnmarshaller() { - memberUnmarshaller = null; - } - - public ObjectSetUnmarshaller(ArgumentUnmarshaller memberUnmarshaller) { - if (memberUnmarshaller == null) { - throw new NullPointerException("memberUnmarshaller"); - } - this.memberUnmarshaller = memberUnmarshaller; - } - - public static ObjectSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) throws ParseException { - List values = value.l(); - - // As in the LinkedHashSet(Collection) constructor. - int size = Math.max(values.size() * 2, 11); - Set objects = new LinkedHashSet(size); - - for (AttributeValue v : values) { - memberUnmarshaller.typeCheck(v, null); - Object o = memberUnmarshaller.unmarshall(v); - if (!objects.add(o)) { - throw new DynamoDbMappingException( - "Duplicate value (" + o + ") found in " + values); - } - } - - return objects; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ObjectUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ObjectUnmarshaller.java deleted file mode 100644 index 9c2415285747..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ObjectUnmarshaller.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.text.ParseException; -import java.util.Map; -import software.amazon.awssdk.services.dynamodb.datamodeling.ItemConverter; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class ObjectUnmarshaller extends MUnmarshaller { - - private static final ObjectUnmarshaller INSTANCE = new ObjectUnmarshaller(); - private final ItemConverter converter; - private final Class clazz; - - private ObjectUnmarshaller() { - converter = null; - clazz = null; - } - - public ObjectUnmarshaller(ItemConverter converter, Class clazz) { - if (converter == null) { - throw new NullPointerException("converter"); - } - if (clazz == null) { - throw new NullPointerException("clazz"); - } - - this.converter = converter; - this.clazz = clazz; - } - - public static ObjectUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) throws ParseException { - Map map = value.m(); - return converter.unconvert(clazz, map); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/S3LinkUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/S3LinkUnmarshaller.java deleted file mode 100644 index 488777f8ee7b..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/S3LinkUnmarshaller.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.datamodeling.S3ClientCache; -import software.amazon.awssdk.services.dynamodb.datamodeling.S3Link; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -public class S3LinkUnmarshaller extends SUnmarshaller { - - private static final S3LinkUnmarshaller INSTANCE = new S3LinkUnmarshaller(); - private final S3ClientCache clientCache; - - - private S3LinkUnmarshaller() { - this(null); - } - - public S3LinkUnmarshaller(S3ClientCache clientCache) { - this.clientCache = clientCache; - } - - public static S3LinkUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - if (clientCache == null) { - throw new IllegalStateException( - "Mapper must be constructed with S3 AWS Credentials to " - + "load S3Link"); - } - - return S3Link.fromJson(clientCache, value.s()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/SUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/SUnmarshaller.java deleted file mode 100644 index 385172193459..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/SUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class SUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.s() == null) { - throw new DynamoDbMappingException("Expected S in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ShortSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ShortSetUnmarshaller.java deleted file mode 100644 index 889eb5a1588c..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ShortSetUnmarshaller.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB NumberSets into sets of Java - * {@code Short}s. - */ -public class ShortSetUnmarshaller extends NsUnmarshaller { - - private static final ShortSetUnmarshaller INSTANCE = - new ShortSetUnmarshaller(); - - private ShortSetUnmarshaller() { - } - - public static ShortSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - Set result = new HashSet(); - for (String s : value.ns()) { - result.add(Short.valueOf(s)); - } - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ShortUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ShortUnmarshaller.java deleted file mode 100644 index d0068a4cccd3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/ShortUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Numbers into Java - * {@code Short}s. - */ -public class ShortUnmarshaller extends NUnmarshaller { - - private static final ShortUnmarshaller INSTANCE = - new ShortUnmarshaller(); - - private ShortUnmarshaller() { - } - - public static ShortUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return Short.valueOf(value.n()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/SsUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/SsUnmarshaller.java deleted file mode 100644 index b8244c1e82a5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/SsUnmarshaller.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.lang.reflect.Method; -import software.amazon.awssdk.services.dynamodb.datamodeling.ArgumentUnmarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -abstract class SsUnmarshaller implements ArgumentUnmarshaller { - - @Override - public void typeCheck(AttributeValue value, Method setter) { - if (value.ss() == null) { - throw new DynamoDbMappingException("Expected SS in value " + value + " when invoking " + setter); - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/StringSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/StringSetUnmarshaller.java deleted file mode 100644 index e53f02a160d1..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/StringSetUnmarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB StringSets as sets of Java - * {@code String} objects. - */ -public class StringSetUnmarshaller extends SsUnmarshaller { - - private static final StringSetUnmarshaller INSTANCE = - new StringSetUnmarshaller(); - - private StringSetUnmarshaller() { - } - - public static StringSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return new HashSet(value.ss()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/StringUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/StringUnmarshaller.java deleted file mode 100644 index cf7195b86fcc..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/StringUnmarshaller.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals DynamoDB Strings as Java {@code String} - * objects. - */ -public class StringUnmarshaller extends SUnmarshaller { - - private static final StringUnmarshaller INSTANCE = - new StringUnmarshaller(); - - private StringUnmarshaller() { - } - - public static StringUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Object unmarshall(AttributeValue value) { - return value.s(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/UuidSetUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/UuidSetUnmarshaller.java deleted file mode 100644 index ceda8d596532..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/UuidSetUnmarshaller.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals sets of UUIDs as sets of - * Java {@code UUID} objects. - */ -public class UuidSetUnmarshaller extends SsUnmarshaller { - - private static final UuidSetUnmarshaller INSTANCE = - new UuidSetUnmarshaller(); - - private UuidSetUnmarshaller() { - } - - public static UuidSetUnmarshaller instance() { - return INSTANCE; - } - - @Override - public Set unmarshall(AttributeValue value) { - Set result = new HashSet(); - - for (String s : value.ss()) { - result.add(UUID.fromString(s)); - } - - return result; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/UuidUnmarshaller.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/UuidUnmarshaller.java deleted file mode 100644 index 32b8f55dd0cb..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/UuidUnmarshaller.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; - -import java.util.UUID; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; - -/** - * An unmarshaller that unmarshals UUIDs as Java - * {@code UUID} objects. - */ -public class UuidUnmarshaller extends SUnmarshaller { - - private static final UuidUnmarshaller INSTANCE = - new UuidUnmarshaller(); - - private UuidUnmarshaller() { - } - - public static UuidUnmarshaller instance() { - return INSTANCE; - } - - @Override - public UUID unmarshall(AttributeValue value) { - return UUID.fromString(value.s()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/package-info.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/package-info.java deleted file mode 100644 index ae581f3569b1..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/datamodeling/unmarshallers/package-info.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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. - */ - -/** - * Standard implementations of {@code ArgumentUnmarshaller}. - */ - -package software.amazon.awssdk.services.dynamodb.datamodeling.unmarshallers; diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/GenerateCreateTableRequestTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/GenerateCreateTableRequestTest.java deleted file mode 100644 index c30986e5d931..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/GenerateCreateTableRequestTest.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; -import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import software.amazon.awssdk.testutils.UnorderedCollectionComparator; -import utils.test.util.DynamoDBTestBase; - -/** - * Tests on the DynamoDBMapper.generateCreateTableRequest method. - */ -public class GenerateCreateTableRequestTest extends DynamoDBTestBase { - - private static DynamoDbMapper mapper; - - @BeforeClass - public static void setUp() { - dynamo = DynamoDbClient.builder() - .credentialsProvider(AnonymousCredentialsProvider.create()) - .region(Region.US_WEST_2) - .build(); - mapper = new DynamoDbMapper(dynamo); - } - - private static boolean equalLsi(Collection a, Collection b) { - return UnorderedCollectionComparator.equalUnorderedCollections(a, b, new LocalSecondaryIndexDefinitionComparator()); - } - - private static boolean equalGsi(Collection a, Collection b) { - return UnorderedCollectionComparator.equalUnorderedCollections(a, b, new GlobalSecondaryIndexDefinitionComparator()); - } - - @Test - @Ignore // No longer works because mapper is not aware of auto construct lists - public void testParseIndexRangeKeyClass() { - CreateTableRequest request = mapper.generateCreateTableRequest(IndexRangeKeyClass.class); - - assertEquals("aws-java-sdk-index-range-test", request.tableName()); - List expectedKeyElements = Arrays.asList( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("rangeKey").keyType(KeyType.RANGE).build() - ); - assertEquals(expectedKeyElements, request.keySchema()); - - List expectedAttrDefinitions = Arrays.asList( - AttributeDefinition.builder().attributeName("key").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("rangeKey").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("indexFooRangeKey").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("indexBarRangeKey").attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName("multipleIndexRangeKey").attributeType(ScalarAttributeType.N).build() - ); - assertTrue(UnorderedCollectionComparator.equalUnorderedCollections( - expectedAttrDefinitions, - request.attributeDefinitions())); - - List expectedLsi = Arrays.asList( - LocalSecondaryIndex.builder() - .indexName("index_foo") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexFooRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("index_bar") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexBarRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("index_foo_copy") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("multipleIndexRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("index_bar_copy") - .keySchema( - KeySchemaElement.builder().attributeName("key").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("multipleIndexRangeKey").keyType(KeyType.RANGE).build()).build()); - assertTrue(equalLsi(expectedLsi, request.localSecondaryIndexes())); - - assertNull(request.globalSecondaryIndexes()); - assertNull(request.provisionedThroughput()); - } - - @Test - public void testComplexIndexedHashRangeClass() { - CreateTableRequest request = mapper.generateCreateTableRequest(MapperQueryExpressionTest.HashRangeClass.class); - - assertEquals("table_name", request.tableName()); - List expectedKeyElements = Arrays.asList( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("primaryRangeKey").keyType(KeyType.RANGE).build() - ); - assertEquals(expectedKeyElements, request.keySchema()); - - List expectedAttrDefinitions = Arrays.asList( - AttributeDefinition.builder().attributeName("primaryHashKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("indexHashKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("primaryRangeKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("indexRangeKey").attributeType(ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName("anotherIndexRangeKey").attributeType(ScalarAttributeType.S).build() - ); - assertTrue(UnorderedCollectionComparator.equalUnorderedCollections( - expectedAttrDefinitions, - request.attributeDefinitions())); - - List expectedLsi = Arrays.asList( - LocalSecondaryIndex.builder() - .indexName("LSI-primary-range") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("primaryRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("LSI-index-range-1") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("LSI-index-range-2") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - LocalSecondaryIndex.builder() - .indexName("LSI-index-range-3") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("anotherIndexRangeKey").keyType(KeyType.RANGE).build()).build()); - assertTrue(equalLsi(expectedLsi, request.localSecondaryIndexes())); - - List expectedGsi = Arrays.asList( - GlobalSecondaryIndex.builder() - .indexName("GSI-primary-hash-index-range-1") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-primary-hash-index-range-2") - .keySchema( - KeySchemaElement.builder().attributeName("primaryHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("anotherIndexRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-index-hash-primary-range") - .keySchema( - KeySchemaElement.builder().attributeName("indexHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("primaryRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-index-hash-index-range-1") - .keySchema( - KeySchemaElement.builder().attributeName("indexHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build(), - GlobalSecondaryIndex.builder() - .indexName("GSI-index-hash-index-range-2") - .keySchema( - KeySchemaElement.builder().attributeName("indexHashKey").keyType(KeyType.HASH).build(), - KeySchemaElement.builder().attributeName("indexRangeKey").keyType(KeyType.RANGE).build()).build()); - assertTrue(equalGsi(expectedGsi, request.globalSecondaryIndexes())); - - assertNull(request.provisionedThroughput()); - } - - private static class LocalSecondaryIndexDefinitionComparator - implements - UnorderedCollectionComparator.CrossTypeComparator { - - @Override - public boolean equals(LocalSecondaryIndex a, LocalSecondaryIndex b) { - return a.indexName().equals(b.indexName()) - && a.keySchema().equals(b.keySchema()); - } - - } - - private static class GlobalSecondaryIndexDefinitionComparator - implements - UnorderedCollectionComparator.CrossTypeComparator { - - @Override - public boolean equals(GlobalSecondaryIndex a, GlobalSecondaryIndex b) { - return a.indexName().equals(b.indexName()) - && a.keySchema().equals(b.keySchema()); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/IndexRangeKeyClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/IndexRangeKeyClass.java deleted file mode 100644 index 9364b6134d09..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/IndexRangeKeyClass.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbVersionAttribute; - -/** - * Comprehensive domain class - */ -@DynamoDbTable(tableName = "aws-java-sdk-index-range-test") -public class IndexRangeKeyClass { - - private long key; - private double rangeKey; - private Double indexFooRangeKey; - private Double indexBarRangeKey; - private Double multipleIndexRangeKey; - private Long version; - - private String fooAttribute; - private String barAttribute; - - @DynamoDbHashKey - public long getKey() { - return key; - } - - public void setKey(long key) { - this.key = key; - } - - @DynamoDbRangeKey - public double getRangeKey() { - return rangeKey; - } - - public void setRangeKey(double rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbIndexRangeKey( - localSecondaryIndexName = "index_foo", - attributeName = "indexFooRangeKey" - ) - public Double getIndexFooRangeKeyWithFakeName() { - return indexFooRangeKey; - } - - public void setIndexFooRangeKeyWithFakeName(Double indexFooRangeKey) { - this.indexFooRangeKey = indexFooRangeKey; - } - - @DynamoDbIndexRangeKey( - localSecondaryIndexName = "index_bar" - ) - public Double getIndexBarRangeKey() { - return indexBarRangeKey; - } - - public void setIndexBarRangeKey(Double indexBarRangeKey) { - this.indexBarRangeKey = indexBarRangeKey; - } - - @DynamoDbIndexRangeKey( - localSecondaryIndexNames = {"index_foo_copy", "index_bar_copy"} - ) - public Double getMultipleIndexRangeKey() { - return multipleIndexRangeKey; - } - - public void setMultipleIndexRangeKey(Double multipleIndexRangeKey) { - this.multipleIndexRangeKey = multipleIndexRangeKey; - } - - @DynamoDbAttribute - public String getFooAttribute() { - return fooAttribute; - } - - public void setFooAttribute(String fooAttribute) { - this.fooAttribute = fooAttribute; - } - - @DynamoDbAttribute - public String getBarAttribute() { - return barAttribute; - } - - public void setBarAttribute(String barAttribute) { - this.barAttribute = barAttribute; - } - - @DynamoDbVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((fooAttribute == null) ? 0 : fooAttribute.hashCode()); - result = prime * result + ((barAttribute == null) ? 0 : barAttribute.hashCode()); - result = prime * result + (int) (key ^ (key >>> 32)); - long temp; - temp = Double.doubleToLongBits(rangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(indexFooRangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(indexBarRangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - IndexRangeKeyClass other = (IndexRangeKeyClass) obj; - if (fooAttribute == null) { - if (other.fooAttribute != null) { - return false; - } - } else if (!fooAttribute.equals(other.fooAttribute)) { - return false; - } - if (barAttribute == null) { - if (other.barAttribute != null) { - return false; - } - } else if (!barAttribute.equals(other.barAttribute)) { - return false; - } - if (key != other.key) { - return false; - } - if (Double.doubleToLongBits(rangeKey) != Double.doubleToLongBits(other.rangeKey)) { - return false; - } - if (Double.doubleToLongBits(indexFooRangeKey) != Double.doubleToLongBits(other.indexFooRangeKey)) { - return false; - } - if (Double.doubleToLongBits(indexBarRangeKey) != Double.doubleToLongBits(other.indexBarRangeKey)) { - return false; - } - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "IndexRangeKeyClass [key=" + key + ", rangeKey=" + rangeKey + ", version=" + version - + ", indexFooRangeKey=" + indexFooRangeKey + ", indexBarRangeKey=" + indexBarRangeKey - + ", fooAttribute=" + fooAttribute + ", barAttribute=" + barAttribute + "]"; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/MapperQueryExpressionTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/MapperQueryExpressionTest.java deleted file mode 100644 index e122d3377146..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/MapperQueryExpressionTest.java +++ /dev/null @@ -1,554 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.atLeastOnce; - -import java.util.ArrayList; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbQueryExpression; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ComparisonOperator; -import software.amazon.awssdk.services.dynamodb.model.Condition; -import software.amazon.awssdk.services.dynamodb.model.QueryRequest; -import software.amazon.awssdk.services.dynamodb.model.QueryResponse; - - -/** - * Unit test for the private method DynamoDBMapper#createQueryRequestFromExpression - */ -public class MapperQueryExpressionTest { - - private static final String TABLE_NAME = "table_name"; - private static final Condition RANGE_KEY_CONDITION = Condition.builder() - .attributeValueList(AttributeValue.builder().s("some value").build()) - .comparisonOperator(ComparisonOperator.EQ).build(); - - private static DynamoDbClient mockClient; - private static DynamoDbMapper mapper; - - @Before - public void setUp() throws SecurityException, NoSuchMethodException { - mockClient = Mockito.mock(DynamoDbClient.class); - mapper = new DynamoDbMapper(mockClient); - } - - private static QueryRequest testCreateQueryRequestFromExpression( - Class clazz, DynamoDbQueryExpression queryExpression) { - return testCreateQueryRequestFromExpression(clazz, queryExpression, null); - } - - private static QueryRequest testCreateQueryRequestFromExpression( - Class clazz, DynamoDbQueryExpression queryExpression, - String expectedErrorMessage) { - try { - Mockito.when(mockClient.query(any(QueryRequest.class))).thenReturn(QueryResponse.builder().items(new ArrayList<>()).build()); - - mapper.queryPage(clazz, queryExpression, DynamoDbMapperConfig.DEFAULT); - if (expectedErrorMessage != null) { - fail("Exception containing messsage (" - + expectedErrorMessage + ") is expected."); - } - - ArgumentCaptor request = ArgumentCaptor.forClass(QueryRequest.class); - Mockito.verify(mockClient, atLeastOnce()).query(request.capture()); - return request.getValue(); - } catch (RuntimeException e) { - if (expectedErrorMessage != null && e.getMessage() != null) { - assertTrue("Exception message [" + e.getMessage() + "] does not contain " + - "the expected message [" + expectedErrorMessage + "].", - e.getMessage().contains(expectedErrorMessage)); - } else { - e.printStackTrace(); - fail("Internal error when calling createQueryRequestFromExpressio method"); - } - } catch (Exception e) { - fail(e.getMessage()); - } - return null; - } - - /** - * Tests different scenarios of hash-only query - **/ - @Test - public void testHashConditionOnly() { - // Primary hash only - QueryRequest queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", null, null))); - assertTrue(queryRequest.keyConditions().size() == 1); - assertEquals("primaryHashKey", queryRequest.keyConditions().keySet().iterator().next()); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertNull(queryRequest.indexName()); - - // Primary hash used for a GSI - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", null, null)) - .withIndexName("GSI-primary-hash")); - assertTrue(queryRequest.keyConditions().size() == 1); - assertEquals("primaryHashKey", queryRequest.keyConditions().keySet().iterator().next()); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertEquals("GSI-primary-hash", queryRequest.indexName()); - - // Primary hash query takes higher priority then index hash query - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", "bar", null))); - assertTrue(queryRequest.keyConditions().size() == 1); - assertEquals("primaryHashKey", queryRequest.keyConditions().keySet().iterator().next()); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertNull(queryRequest.indexName()); - - // Ambiguous query on multiple index hash keys - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass(null, "bar", "charlie")), - "Ambiguous query expression: More than one index hash key EQ conditions"); - - // Ambiguous query when not specifying index name - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass(null, "bar", null)), - "Ambiguous query expression: More than one GSIs"); - - // Explicitly specify a GSI. - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", "bar", null)) - .withIndexName("GSI-index-hash-1")); - assertTrue(queryRequest.keyConditions().size() == 1); - assertEquals("indexHashKey", queryRequest.keyConditions().keySet().iterator().next()); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("bar").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("indexHashKey")); - assertEquals("GSI-index-hash-1", queryRequest.indexName()); - - // Non-existent GSI - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass("foo", "bar", null)) - .withIndexName("some fake gsi"), - "No hash key condition is applicable to the specified index"); - - // No hash key condition specified - queryRequest = testCreateQueryRequestFromExpression( - HashOnlyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashOnlyClass(null, null, null)), - "Illegal query expression: No hash key condition is found in the query"); - } - - /** - * Tests hash + range query - **/ - @Test - public void testHashAndRangeCondition() { - // Primary hash + primary range - QueryRequest queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION)); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("primaryRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.keyConditions().get("primaryRangeKey")); - assertNull(queryRequest.indexName()); - - // Primary hash + primary range on a LSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION) - .withIndexName("LSI-primary-range")); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("primaryRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.keyConditions().get("primaryRangeKey")); - assertEquals("LSI-primary-range", queryRequest.indexName()); - - // Primary hash + index range used by multiple LSI. But also a GSI hash + range - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION)); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("indexRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.keyConditions().get("indexRangeKey")); - assertEquals("GSI-primary-hash-index-range-1", queryRequest.indexName()); - - - // Primary hash + index range on a LSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("LSI-index-range-1")); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("primaryHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("indexRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.keyConditions().get("indexRangeKey")); - assertEquals("LSI-index-range-1", queryRequest.indexName()); - - // Non-existent LSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("some fake lsi"), - "No range key condition is applicable to the specified index"); - - // Illegal query: Primary hash + primary range on a GSI - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("GSI-index-hash-index-range-1"), - "Illegal query expression: No hash key condition is applicable to the specified index"); - - // GSI hash + GSI range - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass(null, "foo")) - .withRangeKeyCondition("primaryRangeKey", RANGE_KEY_CONDITION)); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("indexHashKey")); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("indexHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("primaryRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.keyConditions().get("primaryRangeKey")); - assertEquals("GSI-index-hash-primary-range", queryRequest.indexName()); - - // Ambiguous query: GSI hash + index range used by multiple GSIs - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass(null, "foo")) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION), - "Illegal query expression: Cannot infer the index name from the query expression."); - - // Explicitly specify the GSI name - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass(null, "foo")) - .withRangeKeyCondition("indexRangeKey", RANGE_KEY_CONDITION) - .withIndexName("GSI-index-hash-index-range-2")); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("indexHashKey")); - assertEquals( - Condition.builder().attributeValueList(AttributeValue.builder().s("foo").build()) - .comparisonOperator(ComparisonOperator.EQ).build(), - queryRequest.keyConditions().get("indexHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("indexRangeKey")); - assertEquals(RANGE_KEY_CONDITION, queryRequest.keyConditions().get("indexRangeKey")); - assertEquals("GSI-index-hash-index-range-2", queryRequest.indexName()); - - // Ambiguous query: (1) primary hash + LSI range OR (2) GSI hash + range - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("anotherIndexRangeKey", RANGE_KEY_CONDITION), - "Ambiguous query expression: Found multiple valid queries:"); - - // Multiple range key conditions specified - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyConditions( - ImmutableMap.of( - "primaryRangeKey", RANGE_KEY_CONDITION, - "indexRangeKey", RANGE_KEY_CONDITION)), - "Illegal query expression: Conditions on multiple range keys"); - - // Using an un-annotated range key - queryRequest = testCreateQueryRequestFromExpression( - HashRangeClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new HashRangeClass("foo", null)) - .withRangeKeyCondition("indexHashKey", RANGE_KEY_CONDITION), - "not annotated with either @DynamoDBRangeKey or @DynamoDBIndexRangeKey."); - } - - @Test - public void testHashOnlyQueryOnHashRangeTable() { - // Primary hash only query on a Hash+Range table - QueryRequest queryRequest = testCreateQueryRequestFromExpression( - LSIRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new LSIRangeKeyClass("foo", null))); - assertTrue(queryRequest.keyConditions().size() == 1); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertNull(queryRequest.indexName()); - - // Hash+Range query on a LSI - queryRequest = testCreateQueryRequestFromExpression( - LSIRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new LSIRangeKeyClass("foo", null)) - .withRangeKeyCondition("lsiRangeKey", RANGE_KEY_CONDITION) - .withIndexName("LSI")); - assertTrue(queryRequest.keyConditions().size() == 2); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertTrue(queryRequest.keyConditions().containsKey("lsiRangeKey")); - assertEquals("LSI", queryRequest.indexName()); - - // Hash-only query on a LSI - queryRequest = testCreateQueryRequestFromExpression( - LSIRangeKeyClass.class, - new DynamoDbQueryExpression() - .withHashKeyValues(new LSIRangeKeyClass("foo", null)) - .withIndexName("LSI")); - assertTrue(queryRequest.keyConditions().size() == 1); - assertTrue(queryRequest.keyConditions().containsKey("primaryHashKey")); - assertEquals("LSI", queryRequest.indexName()); - } - - @DynamoDbTable(tableName = TABLE_NAME) - public final class HashOnlyClass { - - @DynamoDbHashKey - @DynamoDbIndexHashKey( - globalSecondaryIndexNames = "GSI-primary-hash" - ) - private String primaryHashKey; - - @DynamoDbIndexHashKey( - globalSecondaryIndexNames = {"GSI-index-hash-1", "GSI-index-hash-2"} - ) - private String indexHashKey; - - @DynamoDbIndexHashKey( - globalSecondaryIndexNames = {"GSI-another-index-hash"} - ) - private String anotherIndexHashKey; - - public HashOnlyClass(String primaryHashKey, String indexHashKey, String anotherIndexHashKey) { - this.primaryHashKey = primaryHashKey; - this.indexHashKey = indexHashKey; - this.anotherIndexHashKey = anotherIndexHashKey; - } - - public String getPrimaryHashKey() { - return primaryHashKey; - } - - public void setPrimaryHashKey(String primaryHashKey) { - this.primaryHashKey = primaryHashKey; - } - - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - public String getAnotherIndexHashKey() { - return anotherIndexHashKey; - } - - public void setAnotherIndexHashKey(String anotherIndexHashKey) { - this.anotherIndexHashKey = anotherIndexHashKey; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public final class HashRangeClass { - private String primaryHashKey; - private String indexHashKey; - private String primaryRangeKey; - private String indexRangeKey; - private String anotherIndexRangeKey; - - public HashRangeClass(String primaryHashKey, String indexHashKey) { - this.primaryHashKey = primaryHashKey; - this.indexHashKey = indexHashKey; - } - - @DynamoDbHashKey - @DynamoDbIndexHashKey( - globalSecondaryIndexNames = { - "GSI-primary-hash-index-range-1", - "GSI-primary-hash-index-range-2"} - ) - public String getPrimaryHashKey() { - return primaryHashKey; - } - - public void setPrimaryHashKey(String primaryHashKey) { - this.primaryHashKey = primaryHashKey; - } - - @DynamoDbIndexHashKey( - globalSecondaryIndexNames = { - "GSI-index-hash-primary-range", - "GSI-index-hash-index-range-1", - "GSI-index-hash-index-range-2"} - ) - public String getIndexHashKey() { - return indexHashKey; - } - - public void setIndexHashKey(String indexHashKey) { - this.indexHashKey = indexHashKey; - } - - @DynamoDbRangeKey - @DynamoDbIndexRangeKey( - globalSecondaryIndexNames = {"GSI-index-hash-primary-range"}, - localSecondaryIndexName = "LSI-primary-range" - ) - public String getPrimaryRangeKey() { - return primaryRangeKey; - } - - public void setPrimaryRangeKey(String primaryRangeKey) { - this.primaryRangeKey = primaryRangeKey; - } - - @DynamoDbIndexRangeKey( - globalSecondaryIndexNames = { - "GSI-primary-hash-index-range-1", - "GSI-index-hash-index-range-1", - "GSI-index-hash-index-range-2"}, - localSecondaryIndexNames = {"LSI-index-range-1", "LSI-index-range-2"} - ) - public String getIndexRangeKey() { - return indexRangeKey; - } - - public void setIndexRangeKey(String indexRangeKey) { - this.indexRangeKey = indexRangeKey; - } - - @DynamoDbIndexRangeKey( - localSecondaryIndexName = "LSI-index-range-3", - globalSecondaryIndexName = "GSI-primary-hash-index-range-2" - ) - public String getAnotherIndexRangeKey() { - return anotherIndexRangeKey; - } - - public void setAnotherIndexRangeKey(String anotherIndexRangeKey) { - this.anotherIndexRangeKey = anotherIndexRangeKey; - } - } - - @DynamoDbTable(tableName = TABLE_NAME) - public final class LSIRangeKeyClass { - private String primaryHashKey; - private String primaryRangeKey; - private String lsiRangeKey; - - public LSIRangeKeyClass(String primaryHashKey, String primaryRangeKey) { - this.primaryHashKey = primaryHashKey; - this.primaryRangeKey = primaryRangeKey; - } - - @DynamoDbHashKey - public String getPrimaryHashKey() { - return primaryHashKey; - } - - public void setPrimaryHashKey(String primaryHashKey) { - this.primaryHashKey = primaryHashKey; - } - - @DynamoDbRangeKey - public String getPrimaryRangeKey() { - return primaryRangeKey; - } - - public void setPrimaryRangeKey(String primaryRangeKey) { - this.primaryRangeKey = primaryRangeKey; - } - - @DynamoDbIndexRangeKey(localSecondaryIndexName = "LSI") - public String getLsiRangeKey() { - return lsiRangeKey; - } - - public void setLsiRangeKey(String lsiRangeKey) { - this.lsiRangeKey = lsiRangeKey; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/V2CompatibleBooleansTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/V2CompatibleBooleansTest.java deleted file mode 100644 index e4aa0dab9cab..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/mapper/V2CompatibleBooleansTest.java +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.mapper; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.List; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; -import software.amazon.awssdk.utils.ImmutableMap; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchema; -import software.amazon.awssdk.services.dynamodb.datamodeling.ConversionSchemas; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapper; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperConfig; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMapperFieldModel; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMappingException; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbNativeBoolean; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTyped; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; -import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; -import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse; - -/** - * The default converters should be able to unmarshall both V1 booleans (numeric 0/1) and native Dynamo booleans. When using the - * standard converters booleans should be marshalled as numeric attribute values. - */ -@RunWith(MockitoJUnitRunner.class) -public class V2CompatibleBooleansTest { - - private static final String HASH_KEY = "1234"; - - @Mock - private DynamoDbClient ddb; - - /** - * Mapper with default config. - */ - private DynamoDbMapper defaultMapper; - - /** - * Mapper explicitly using {@link ConversionSchemas#V2_COMPATIBLE} - */ - private DynamoDbMapper v2CompatMapper; - - /** - * Mapper explicitly using {@link ConversionSchemas#V1} - */ - private DynamoDbMapper v1Mapper; - - /** - * Mapper explicitly using {@link ConversionSchemas#V2} - */ - private DynamoDbMapper v2Mapper; - - @Before - public void setup() { - defaultMapper = new DynamoDbMapper(ddb); - v2CompatMapper = buildMapper(ConversionSchemas.V2_COMPATIBLE); - v1Mapper = buildMapper(ConversionSchemas.V1); - v2Mapper = buildMapper(ConversionSchemas.V2); - // Just stub dummy response for all save related tests - when(ddb.updateItem(any(UpdateItemRequest.class))).thenReturn(UpdateItemResponse.builder().build()); - } - - private DynamoDbMapper buildMapper(ConversionSchema schema) { - return new DynamoDbMapper(ddb, DynamoDbMapperConfig.builder() - .withConversionSchema(schema) - .build()); - } - - /** - * Without coercion from an annotation the default mapping should marshall booleans as a number. - */ - @Test - public void saveBooleanUsingDefaultConverters_MarshallsIntoNumber() { - defaultMapper.save(new UnitTestPojo().setHashKey(HASH_KEY).setBooleanAttr(true)); - verifyAttributeUpdatedWithValue("booleanAttr", AttributeValue.builder().n("1").build()); - } - - @Test - public void saveBooleanUsingV1Schema_MarshallsIntoNumber() { - v1Mapper.save(new UnitTestPojo().setHashKey(HASH_KEY).setBooleanAttr(true)); - verifyAttributeUpdatedWithValue("booleanAttr", AttributeValue.builder().n("1").build()); - } - - @Test - public void saveBooleanUsingV2Compat_MarshallsIntoNumber() { - v2CompatMapper.save(new UnitTestPojo().setHashKey(HASH_KEY).setBooleanAttr(true)); - verifyAttributeUpdatedWithValue("booleanAttr", AttributeValue.builder().n("1").build()); - } - - @Test - public void saveBooleanUsingV2Schema_MarshallsIntoNativeBool() { - v2Mapper.save(new UnitTestPojo().setHashKey(HASH_KEY).setBooleanAttr(true)); - verifyAttributeUpdatedWithValue("booleanAttr", AttributeValue.builder().bool(true).build()); - } - - /** - * {@link DynamoDbNativeBoolean} or {@link DynamoDbTyped} can force - * native - * boolean marshalling. - */ - @Test - public void saveCoercedNativeBooleanUsingDefaultConverters_MarshallsIntoNativeBool() { - saveCoercedNativeBoolean_MarshallsIntoNativeBoolean(defaultMapper); - } - - @Test - public void saveCoercedNativeBooleanUsingV1Schema_MarshallsIntoNativeBool() { - saveCoercedNativeBoolean_MarshallsIntoNativeBoolean(v1Mapper); - } - - @Test - public void saveCoercedNativeBooleanUsingV2CompatSchema_MarshallsIntoNativeBool() { - saveCoercedNativeBoolean_MarshallsIntoNativeBoolean(v2CompatMapper); - } - - @Test - public void saveCoercedNativeBooleanUsingV2_MarshallsIntoNativeBool() { - saveCoercedNativeBoolean_MarshallsIntoNativeBoolean(v2Mapper); - } - - private void saveCoercedNativeBoolean_MarshallsIntoNativeBoolean(DynamoDbMapper mapper) { - mapper.save(new UnitTestPojo().setNativeBoolean(true).setHashKey(HASH_KEY)); - verifyAttributeUpdatedWithValue("nativeBoolean", AttributeValue.builder().bool(true).build()); - } - - /** - * {@link DynamoDbTyped} can force numeric boolean conversion even when using V2 schema. - */ - @Test - public void saveCoercedNumericBooleanUsingDefaultConverters_MarshallsIntoNumericBool() { - saveCoercedNumericBoolean_MarshallsIntoNumericBoolean(defaultMapper); - } - - @Test - public void saveCoercedNumericBooleanUsingV1Schema_MarshallsIntoNumericBool() { - saveCoercedNumericBoolean_MarshallsIntoNumericBoolean(v1Mapper); - } - - @Test - public void saveCoercedNumericBooleanUsingV2CompatSchema_MarshallsIntoNumericBool() { - saveCoercedNumericBoolean_MarshallsIntoNumericBoolean(v2CompatMapper); - } - - @Test - public void saveCoercedNumericBooleanUsingV2_MarshallsIntoNumericBool() { - saveCoercedNumericBoolean_MarshallsIntoNumericBoolean(v2Mapper); - } - - private void saveCoercedNumericBoolean_MarshallsIntoNumericBoolean(DynamoDbMapper mapper) { - mapper.save(new UnitTestPojo().setNumericBoolean(true).setHashKey(HASH_KEY)); - verifyAttributeUpdatedWithValue("numericBoolean", AttributeValue.builder().n("1").build()); - } - - @Test - public void saveBooleanListUsingDefaultConverters_MarshallsIntoListOfNumbers() { - defaultMapper.save(new UnitTestPojoWithList() - .setBooleanList(Arrays.asList(Boolean.FALSE, Boolean.TRUE)) - .setHashKey(HASH_KEY)); - verifyAttributeUpdatedWithValue("booleanList", AttributeValue.builder() - .l( - AttributeValue.builder().n("0").build(), - AttributeValue.builder().n("1").build()) - .build()); - } - - /** - * Verifies the mapper results in an update item call that has an update for the appropriate attribute. - * - * @param attributeName Attribute expected to be updated. - * @param expected Expected value of update action. - */ - private void verifyAttributeUpdatedWithValue(String attributeName, AttributeValue expected) { - ArgumentCaptor updateItemRequestCaptor = ArgumentCaptor.forClass(UpdateItemRequest.class); - verify(ddb).updateItem(updateItemRequestCaptor.capture()); - assertEquals(expected, updateItemRequestCaptor.getValue().attributeUpdates().get(attributeName).value()); - } - - @Test - public void loadNumericBooleanUsingDefaultConverters_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().n("1").build()); - final UnitTestPojo pojo = loadPojo(defaultMapper); - assertTrue(pojo.getBooleanAttr()); - } - - @Test - public void loadNumericBooleanUsingV1Schema_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().n("1").build()); - final UnitTestPojo pojo = loadPojo(v1Mapper); - assertTrue(pojo.getBooleanAttr()); - } - - @Test - public void loadNumericBooleanUsingV2CompatSchema_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().n("1").build()); - final UnitTestPojo pojo = loadPojo(v2CompatMapper); - assertTrue(pojo.getBooleanAttr()); - } - - @Test - public void loadNumericBooleanUsingV2_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().n("1").build()); - final UnitTestPojo pojo = loadPojo(v2Mapper); - assertTrue(pojo.getBooleanAttr()); - } - - @Test - public void loadNativeBooleanUsingDefaultConverters_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().bool(true).build()); - final UnitTestPojo pojo = loadPojo(defaultMapper); - assertTrue(pojo.getBooleanAttr()); - } -// - /** - * V1 schema does not handle native bool types by default - */ - @Test(expected = DynamoDbMappingException.class) - public void loadNativeBooleanUsingV1Schema_FailsToUnmarshall() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().bool(true).build()); - loadPojo(v1Mapper); - } - - /** - * Native bool support can be forced in V1 schema with @{@link DynamoDbTyped}. - */ - @Test - public void loadCoercedNativeBooleanUsingV1Schema_UnmarshallsCorrectly() { - stubGetItemRequest("nativeBoolean", AttributeValue.builder().bool(true).build()); - final UnitTestPojo pojo = loadPojo(v1Mapper); - assertTrue(pojo.getNativeBoolean()); - } - - @Test - public void loadNativeBooleanUsingV2CompatSchema_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().bool(true).build()); - final UnitTestPojo pojo = loadPojo(v2CompatMapper); - assertTrue(pojo.getBooleanAttr()); - } - - @Test - public void loadNativeBooleanUsingV2_UnmarshallsCorrectly() { - stubGetItemRequest("booleanAttr", AttributeValue.builder().bool(true).build()); - final UnitTestPojo pojo = loadPojo(v2Mapper); - assertTrue(pojo.getBooleanAttr()); - } - - @Test - public void loadNativeBooleanListUsingDefaultConverters_UnmarshallsCorrectly() { - stubGetItemRequest("booleanList", AttributeValue.builder() - .l( - AttributeValue.builder().bool(true).build(), - AttributeValue.builder().bool(false).build() - ).build()); - final UnitTestPojoWithList pojo = loadListPojo(defaultMapper); - - assertTrue(pojo.getBooleanList().get(0)); - assertFalse(pojo.getBooleanList().get(1)); - } - - @Test - public void loadNumericBooleanListUsingDefaultConverters_UnmarshallsCorrectly() { - stubGetItemRequest("booleanList", AttributeValue.builder() - .l( - AttributeValue.builder().n("1").build(), - AttributeValue.builder().n("0").build() - ).build()); - final UnitTestPojoWithList pojo = loadListPojo(defaultMapper); - - assertTrue(pojo.getBooleanList().get(0)); - assertFalse(pojo.getBooleanList().get(1)); - } - - private UnitTestPojoWithList loadListPojo(DynamoDbMapper mapper) { - UnitTestPojoWithList pojo = new UnitTestPojoWithList(); - pojo.setHashKey(HASH_KEY); - return mapper.load(pojo); - } - - private UnitTestPojo loadPojo(DynamoDbMapper mapper) { - return mapper.load(new UnitTestPojo().setHashKey(HASH_KEY)); - } - - /** - * Stub a call to getItem to return a result with the given attribute value in the item. - * - * @param attributeName Attribute name to return in result (in addition to hash key) - * @param attributeValue Attribute value to return in result (in addition to hash key) - */ - private void stubGetItemRequest(String attributeName, AttributeValue attributeValue) { - when(ddb.getItem(any(GetItemRequest.class))).thenReturn(createGetItemResponse(attributeName, attributeValue)); - } - - /** - * Create a {@link GetItemResponse} with the hash key value ({@value #HASH_KEY} and the additional attribute. - * - * @param attributeName Additional attribute to include in created {@link GetItemResponse}. - * @param attributeValue Value of additional attribute. - */ - private GetItemResponse createGetItemResponse(String attributeName, AttributeValue attributeValue) { - return GetItemResponse.builder().item( - ImmutableMap.of("hashKey", AttributeValue.builder().s(HASH_KEY).build(), - attributeName, attributeValue)).build(); - } - - @DynamoDbTable(tableName = "UnitTestTable") - public static class UnitTestPojo { - - @DynamoDbHashKey - private String hashKey; - - @DynamoDbAttribute - private Boolean booleanAttr; - - @DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.BOOL) - private Boolean nativeBoolean; - - @DynamoDbTyped(DynamoDbMapperFieldModel.DynamoDbAttributeType.N) - private Boolean numericBoolean; - - - public String getHashKey() { - return hashKey; - } - - public UnitTestPojo setHashKey(String hashKey) { - this.hashKey = hashKey; - return this; - } - - public Boolean getBooleanAttr() { - return booleanAttr; - } - - public UnitTestPojo setBooleanAttr(Boolean booleanAttr) { - this.booleanAttr = booleanAttr; - return this; - } - - public Boolean getNativeBoolean() { - return nativeBoolean; - } - - public UnitTestPojo setNativeBoolean(Boolean nativeBoolean) { - this.nativeBoolean = nativeBoolean; - return this; - } - - public Boolean getNumericBoolean() { - return numericBoolean; - } - - public UnitTestPojo setNumericBoolean(Boolean numericBoolean) { - this.numericBoolean = numericBoolean; - return this; - } - - } - - public static class UnitTestPojoWithList extends UnitTestPojo { - - @DynamoDbAttribute - private List booleanList; - - public List getBooleanList() { - return booleanList; - } - - public UnitTestPojo setBooleanList(List booleanList) { - this.booleanList = booleanList; - return this; - } - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/model/Item.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/model/Item.java deleted file mode 100644 index c1f9d713e591..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/model/Item.java +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.model; - -import static java.util.stream.Collectors.toList; -import static software.amazon.awssdk.utils.CollectionUtils.toMap; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import software.amazon.awssdk.core.SdkBytes; -import software.amazon.awssdk.utils.builder.SdkBuilder; - -/** - * Used to build a DynamoDB item map. - * - * For example to pass to a {@link PutItemRequest.Builder#item(Map)}. - */ -//TODO : May want to do this a different way -public final class Item extends HashMap { - - private Item(Builder builder) { - putAll(builder.item); - } - - /** - * Create a new instance of the {@link Builder}. - * - * @return a new instance of the {@link Builder} - */ - public static Builder builder() { - return new Builder(); - } - - public static class Builder implements SdkBuilder { - private final Map item = new HashMap<>(); - - private Builder() { - } - - /** - * Build a {@link Map} representing a DyanmoDB item. - * - * @return a {@link Map} representing a DyanmoDB item - */ - @Override - public Item build() { - return new Item(this); - } - - /** - * Adds an {@link AttributeValue} representing a String to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().s(stringValue).build());
    -         * 
    - * - * @param key the key of this attribute - * @param stringValue the string value of the attribute - * @return the builder for method chaining - */ - public Builder attribute(String key, String stringValue) { - item.put(key, AttributeValue.builder().s(stringValue).build()); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a Boolean to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().bool(booleanValue).build());
    -         * 
    - * - * @param key the key of this attribute - * @param booleanValue the boolean value of the attribute - * @return the builder for method chaining - */ - public Builder attribute(String key, Boolean booleanValue) { - item.put(key, AttributeValue.builder().bool(booleanValue).build()); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a Number to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().n(String.valueOf(numericValue)).build());
    -         * 
    - * - * @param key the key of this attribute - * @param numericValue the numeric value of the attribute - * @return the builder for method chaining - */ - public Builder attribute(String key, Number numericValue) { - item.put(key, AttributeValue.builder().n(String.valueOf(numericValue)).build()); - return this; - } - - /** - * Adds an {@link AttributeValue} representing binary data to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().b(ByteBuffer.wrap(binaryValue)).build());
    -         * 
    - * - * @param key the key of this attribute - * @param binaryValue the binary value of the attribute - * @return the builder for method chaining - */ - public Builder attribute(String key, byte[] binaryValue) { - return attribute(key, ByteBuffer.wrap(binaryValue)); - } - - /** - * Adds an {@link AttributeValue} representing binary data to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().b(binaryValue).build());
    -         * 
    - * - * @param key the key of this attribute - * @param binaryValue the binary value of the attribute - * @return the builder for method chaining - */ - public Builder attribute(String key, ByteBuffer binaryValue) { - item.put(key, AttributeValue.builder().b(SdkBytes.fromByteBuffer(binaryValue)).build()); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a list of AttributeValues to the item with the specified key. - * - * This will attempt to infer the of of the {@link AttributeValue} for each given {@link Object} in the list - * based on the type. Supported types are: - *
      - *
    • {@link AttributeValue} which is unaltered
    • - *
    • {@link String} becomes {@link AttributeValue#s()}
    • - *
    • {@link Number} (and anything that can be automatically boxed to {@link Number} including - * int, long, float etc.) becomes {@link AttributeValue#n()}
    • - *
    • {@link Boolean} (and bool) becomes {@link AttributeValue#bool()}
    • - *
    • byte[] becomes {@link AttributeValue#b()}
    • - *
    • {@link ByteBuffer} becomes {@link AttributeValue#b()}
    • - *
    • {@link List}<Object> (where the containing objects are one of the types in - * this list) becomes {@link AttributeValue#l()}
    • - *
    • {@link Map}<String, Object> (where the containing object values are one of the types - * in this list) becomes {@link AttributeValue#m()}
    • - *
    - * - * @param key the key of this attribute - * @param values the object values - * @return the builder for method chaining - */ - public Builder attribute(String key, List values) { - item.put(key, fromObject(values)); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a map of string to AttributeValues to the item with the specified key. - * - * This will attempt to infer the most appropriate {@link AttributeValue} for each given {@link Object} value in the map - * based on the type. Supported types are: - *
      - *
    • {@link AttributeValue} which is unaltered
    • - *
    • {@link String} becomes {@link AttributeValue#s()}
    • - *
    • {@link Number} (and anything that can be automatically boxed to {@link Number} including - * int, long, float etc.) becomes {@link AttributeValue#n()}
    • - *
    • {@link Boolean} (and bool) becomes {@link AttributeValue#bool()}
    • - *
    • byte[] becomes {@link AttributeValue#b()}
    • - *
    • {@link ByteBuffer} becomes {@link AttributeValue#b()}
    • - *
    • {@link List}<Object> (where the containing objects are one of the types in - * this list) becomes {@link AttributeValue#l()}
    • - *
    • {@link Map}<String, Object> (where the containing object values are one of the types - * in this list) becomes {@link AttributeValue#m()}
    • - *
    - * - * @param key the key of this attribute - * @param values the map of key to object - * @return the builder for method chaining - */ - public Builder attribute(String key, Map values) { - item.put(key, fromObject(values)); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a list of strings to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().ss(stringValue1, stringValue2, ...).build());
    -         * 
    - * - * @param key the key of this attribute - * @param stringValues the string values of the attribute - * @return the builder for method chaining - */ - public Builder strings(String key, String... stringValues) { - return strings(key, Arrays.asList(stringValues)); - } - - /** - * Adds an {@link AttributeValue} representing a list of strings to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().ss(stringValues).build());
    -         * 
    - * - * @param key the key of this attribute - * @param stringValues the string values of the attribute - * @return the builder for method chaining - */ - public Builder strings(String key, Collection stringValues) { - item.put(key, AttributeValue.builder().ss(stringValues).build()); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a list of numbers to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().ns(numberValues1, numberValues2, ...).build());
    -         * 
    - * - * @param key the key of this attribute - * @param numberValues the number values of the attribute - * @return the builder for method chaining - */ - public Builder numbers(String key, Number... numberValues) { - return numbers(key, Arrays.asList(numberValues)); - } - - /** - * Adds an {@link AttributeValue} representing a list of numbers to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().ns(numberValues).build());
    -         * 
    - * - * @param key the key of this attribute - * @param numberValues the number values of the attribute - * @return the builder for method chaining - */ - public Builder numbers(String key, Collection numberValues) { - item.put(key, AttributeValue.builder().ns(numberValues.stream().map(String::valueOf).collect(toList())).build()); - return this; - } - - /** - * Adds an {@link AttributeValue} representing a list of binary data to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().bs(Arrays.stream(byteArrays)
    -         *                                                    .map(ByteBuffer::wrap)
    -         *                                                    .collect(Collectors.toList())).build());
    -         * 
    - * - * @param key the key of this attribute - * @param byteArrays the binary values of the attribute - * @return the builder for method chaining - */ - public Builder byteArrays(String key, byte[]... byteArrays) { - return byteArrays(key, Arrays.asList(byteArrays)); - } - - /** - * Adds an {@link AttributeValue} representing a list of binary data to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().bs(byteArrays.stream()
    -         *                                                        .map(ByteBuffer::wrap)
    -         *                                                        .collect(Collectors.toList())).build());
    -         * 
    - * - * @param key the key of this attribute - * @param byteArrays the binary values of the attribute - * @return the builder for method chaining - */ - public Builder byteArrays(String key, Collection byteArrays) { - return byteBuffers(key, byteArrays.stream().map(ByteBuffer::wrap).collect(Collectors.toList())); - } - - /** - * Adds an {@link AttributeValue} representing a list of binary data to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().bs(binaryValues1, binaryValues2, ...)).build());
    -         * 
    - * - * @param key the key of this attribute - * @param binaryValues the binary values of the attribute - * @return the builder for method chaining - */ - public Builder byteBuffers(String key, ByteBuffer... binaryValues) { - return byteBuffers(key, Arrays.asList(binaryValues)); - } - - /** - * Adds an {@link AttributeValue} representing a list of binary data to the item with the specified key. - * - * Equivalent of: - *
    
    -         * itemMap.put(key, AttributeValue.builder().bs(binaryValues).build());
    -         * 
    - * - * @param key the key of this attribute - * @param binaryValues the binary values of the attribute - * @return the builder for method chaining - */ - public Builder byteBuffers(String key, Collection binaryValues) { - item.put(key, AttributeValue.builder().bs(binaryValues.stream().map(SdkBytes::fromByteBuffer).collect(toList())).build()); - return this; - } - - private static AttributeValue fromObject(Object object) { - if (object instanceof AttributeValue) { - return (AttributeValue) object; - } - if (object instanceof String) { - return AttributeValue.builder().s((String) object).build(); - } - if (object instanceof Number) { - return AttributeValue.builder().n(String.valueOf((Number) object)).build(); - } - if (object instanceof byte[]) { - return AttributeValue.builder().b(SdkBytes.fromByteArray((byte[]) object)).build(); - } - if (object instanceof ByteBuffer) { - return AttributeValue.builder().b(SdkBytes.fromByteBuffer((ByteBuffer) object)).build(); - } - if (object instanceof Boolean) { - return AttributeValue.builder().bool((Boolean) object).build(); - } - if (object instanceof List) { - List attributeValues = ((List) object).stream() - .map(Builder::fromObject) - .collect(toList()); - return AttributeValue.builder().l(attributeValues).build(); - } - if (object instanceof Map) { - Map attributeValues = - ((Map) object).entrySet() - .stream() - .map(e -> new SimpleImmutableEntry<>(e.getKey(), fromObject(e.getValue()))) - .collect(toMap()); - return AttributeValue.builder().m(attributeValues).build(); - } - throw new IllegalArgumentException("Unsupported type: " + object.getClass()); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/model/ItemTest.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/model/ItemTest.java deleted file mode 100644 index ad980c4837a5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/model/ItemTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.model; - -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.util.Collections.singletonMap; -import static org.assertj.core.api.Assertions.assertThat; - -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.assertj.core.api.Condition; -import org.junit.Test; - -public class ItemTest { - - public static final byte[] BYTES1 = "value1".getBytes(UTF_8); - public static final byte[] BYTES2 = "value2".getBytes(UTF_8); - - @Test - public void testItemBuilder() { - - Map testMap = new HashMap<>(); - testMap.put("String", "stringValue"); - testMap.put("int", 15); - testMap.put("byteArray", BYTES1); - testMap.put("ByteBuffer", ByteBuffer.wrap(BYTES2)); - testMap.put("bool", true); - - List longs = Arrays.asList(1l, 2l, 3l); - - Map item = - Item.builder() - .attribute("String", "stringValue") - .attribute("int", 15) - .attribute("long", 200L) - .attribute("float", 15.5) - .attribute("Integer", new Integer(15)) - .attribute("Long", new Long(200)) - .attribute("Float", new Float(15.5)) - .attribute("bool", true) - .attribute("Boolean", Boolean.TRUE) - .attribute("bytes", BYTES1) - .attribute("ByteBuffer", ByteBuffer.wrap(BYTES1)) - .strings("StringsVarArgs", "value1", "value2") - .strings("StringsCollection", Arrays.asList("value1", "value2")) - .numbers("NumbersVarArgs", 1, 2.0, 3L) - .numbers("NumbersCollection", Arrays.asList(1, 2.0, 3L)) - .numbers("LongCollection", longs) - .byteArrays("bytesVarArgs", BYTES1, BYTES2) - .byteArrays("bytesCollection", Arrays.asList(BYTES1, BYTES2)) - .byteBuffers("ByteBuffersVarArgs", ByteBuffer.wrap(BYTES1), ByteBuffer.wrap(BYTES2)) - .byteBuffers("ByteBuffersCollection", Arrays.asList(ByteBuffer.wrap(BYTES1), ByteBuffer.wrap(BYTES2))) - .attribute("list", Arrays.asList(15, 200L, 15.5, "stringValue", BYTES1, ByteBuffer.wrap(BYTES2), true)) - .attribute("map", testMap) - .attribute("nested", Arrays.asList(15, singletonMap("nestedList", Arrays.asList("stringValue", BYTES1)))) - .build(); - - - assertThat(item).containsEntry("String", AttributeValue.builder().s("stringValue").build()); - assertThat(item).containsEntry("int", AttributeValue.builder().n("15").build()); - assertThat(item).containsEntry("long", AttributeValue.builder().n("200").build()); - assertThat(item).containsEntry("float", AttributeValue.builder().n("15.5").build()); - assertThat(item).containsEntry("Integer", AttributeValue.builder().n("15").build()); - assertThat(item).containsEntry("Long", AttributeValue.builder().n("200").build()); - assertThat(item).containsEntry("Float", AttributeValue.builder().n("15.5").build()); - assertThat(item).containsEntry("bool", AttributeValue.builder().bool(true).build()); - assertThat(item).containsEntry("Boolean", AttributeValue.builder().bool(true).build()); - assertThat(item).containsEntry("StringsVarArgs", AttributeValue.builder().ss("value1", "value2").build()); - assertThat(item).containsEntry("StringsCollection", AttributeValue.builder().ss("value1", "value2").build()); - assertThat(item).containsEntry("NumbersVarArgs", AttributeValue.builder().ns("1", "2.0", "3").build()); - assertThat(item).containsEntry("NumbersCollection", AttributeValue.builder().ns("1", "2.0", "3").build()); - assertThat(item).containsEntry("LongCollection", AttributeValue.builder().ns("1", "2", "3").build()); - - assertThat(item).hasEntrySatisfying("bytes", bytesMatching(BYTES1)); - assertThat(item).hasEntrySatisfying("ByteBuffer", bytesMatching(BYTES1)); - assertThat(item).hasEntrySatisfying("bytesVarArgs", bytesMatching(BYTES1, BYTES2)); - assertThat(item).hasEntrySatisfying("bytesCollection", bytesMatching(BYTES1, BYTES2)); - assertThat(item).hasEntrySatisfying("ByteBuffersVarArgs", bytesMatching(BYTES1, BYTES2)); - assertThat(item).hasEntrySatisfying("ByteBuffersCollection", bytesMatching(BYTES1, BYTES2)); - - assertThat(item.get("list").l()).hasSize(7); - assertThat(item.get("list").l().get(0)).isEqualTo(AttributeValue.builder().n("15").build()); - assertThat(item.get("list").l().get(1)).isEqualTo(AttributeValue.builder().n("200").build()); - assertThat(item.get("list").l().get(2)).isEqualTo(AttributeValue.builder().n("15.5").build()); - assertThat(item.get("list").l().get(3)).isEqualTo(AttributeValue.builder().s("stringValue").build()); - assertThat(item.get("list").l().get(4)).has(bytesMatching(BYTES1)); - assertThat(item.get("list").l().get(5)).has(bytesMatching(BYTES2)); - assertThat(item.get("list").l().get(6)).isEqualTo(AttributeValue.builder().bool(true).build()); - - assertThat(item.get("map").m().entrySet()).hasSize(5); - assertThat(item.get("map").m().get("String")).isEqualTo(AttributeValue.builder().s("stringValue").build()); - assertThat(item.get("map").m().get("int")).isEqualTo(AttributeValue.builder().n("15").build()); - assertThat(item.get("map").m().get("byteArray")).has(bytesMatching(BYTES1)); - assertThat(item.get("map").m().get("ByteBuffer")).has(bytesMatching(BYTES2)); - assertThat(item.get("map").m().get("bool")).isEqualTo(AttributeValue.builder().bool(true).build()); - - assertThat(item.get("nested").l()).hasSize(2); - assertThat(item.get("nested").l().get(0)).isEqualTo(AttributeValue.builder().n("15").build()); - assertThat(item.get("nested").l().get(1).m().entrySet()).hasSize(1); - assertThat(item.get("nested").l().get(1).m().get("nestedList").l()).hasSize(2); - assertThat(item.get("nested").l().get(1).m().get("nestedList").l().get(0)).isEqualTo(AttributeValue.builder().s("stringValue").build()); - assertThat(item.get("nested").l().get(1).m().get("nestedList").l().get(1)).has(bytesMatching(BYTES1)); - } - - private static Condition bytesMatching(byte[] bytes) { - return new Condition<>(item -> byteBufferEquals(item.b().asByteBuffer(), bytes), "bytes matching"); - } - - private static Condition bytesMatching(byte[] firstByteArray, byte[] secondByteArray) { - return new Condition<>(item -> { - return item.bs().size() == 2 && - byteBufferEquals(item.bs().get(0).asByteBuffer(), firstByteArray) && - byteBufferEquals(item.bs().get(1).asByteBuffer(), secondByteArray); - }, "List<" + String.valueOf(firstByteArray) + ", " + String.valueOf(secondByteArray) + ">"); - } - - private static boolean byteBufferEquals(ByteBuffer byteBuffer, byte[] bytes) { - if (byteBuffer.remaining() != bytes.length) { - return false; - } - byte[] actual = new byte[bytes.length]; - byteBuffer.duplicate().get(actual); - return Arrays.equals(actual, bytes); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/AuditRecord.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/AuditRecord.java deleted file mode 100644 index 3cd0a6efb1be..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/AuditRecord.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.util.Date; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGenerateStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedTimestamp; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbFlattened; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbVersionAttribute; - -@DynamoDbFlattened(attributes = {@DynamoDbAttribute(mappedBy = "createdDate", attributeName = "RCD"), - @DynamoDbAttribute(mappedBy = "lastModifiedDate", attributeName = "RMD"), - @DynamoDbAttribute(mappedBy = "versionNumber", attributeName = "RVN")}) -public class AuditRecord { - - private Date createdDate; - private Date lastModifiedDate; - private Long versionNumber; - - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.CREATE) - public Date getCreatedDate() { - return this.createdDate; - } - - public void setCreatedDate(final Date createdDate) { - this.createdDate = createdDate; - } - - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.ALWAYS) - public Date getLastModifiedDate() { - return this.lastModifiedDate; - } - - public void setLastModifiedDate(final Date lastModifiedDate) { - this.lastModifiedDate = lastModifiedDate; - } - - @DynamoDbVersionAttribute - public Long getVersionNumber() { - return this.versionNumber; - } - - public void setVersionNumber(final Long versionNumber) { - this.versionNumber = versionNumber; - } - - @Override - public final boolean equals(final Object o) { - if (o == this) { - return true; - } - if (!(o instanceof AuditRecord)) { - return false; - } - AuditRecord that = (AuditRecord) o; - return (createdDate == null ? that.createdDate == null : createdDate.equals(that.createdDate)) && - (lastModifiedDate == null ? that.lastModifiedDate == null : lastModifiedDate.equals(that.lastModifiedDate)) && - (versionNumber == null ? that.versionNumber == null : versionNumber.equals(that.versionNumber)); - } - - @Override - public final int hashCode() { - return 1 + (createdDate == null ? 0 : createdDate.hashCode()) + - (lastModifiedDate == null ? 0 : lastModifiedDate.hashCode()) + - (versionNumber == null ? 0 : versionNumber.hashCode()); - } - - @Override - public final String toString() { - return getClass().getName() + "{" + - "createdDate=" + createdDate + "," + - "lastModifiedDate=" + lastModifiedDate + "," + - "versionNumber=" + versionNumber + - "}"; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/AutoKeyAndVal.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/AutoKeyAndVal.java deleted file mode 100644 index 18806bb29d15..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/AutoKeyAndVal.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Auto-generated string key and value. - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class AutoKeyAndVal extends KeyAndVal { - @Override - @DynamoDbHashKey - @DynamoDbAutoGeneratedKey - public String getKey() { - return super.getKey(); - } - - @Override - public void setKey(final String key) { - super.setKey(key); - } - - @Override - public V getVal() { - return super.getVal(); - } - - @Override - public void setVal(final V val) { - super.setVal(val); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/BinaryAttributeByteArrayClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/BinaryAttributeByteArrayClass.java deleted file mode 100644 index e4f575ee529e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/BinaryAttributeByteArrayClass.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Test domain class with byte[] attribute, byte[] set and a string key - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class BinaryAttributeByteArrayClass { - - private String key; - private byte[] binaryAttribute; - private Set binarySetAttribute; - - @DynamoDbHashKey(attributeName = "key") - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute(attributeName = "binaryAttribute") - public byte[] getBinaryAttribute() { - return binaryAttribute; - } - - public void setBinaryAttribute(byte[] binaryAttribute) { - this.binaryAttribute = binaryAttribute; - } - - @DynamoDbAttribute(attributeName = "binarySetAttribute") - public Set getBinarySetAttribute() { - return binarySetAttribute; - } - - public void setBinarySetAttribute(Set binarySetAttribute) { - this.binarySetAttribute = binarySetAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((binaryAttribute == null) ? 0 : binaryAttribute.hashCode()); - result = prime * result + ((binarySetAttribute == null) ? 0 : binarySetAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - BinaryAttributeByteArrayClass other = (BinaryAttributeByteArrayClass) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (binaryAttribute == null) { - if (other.binaryAttribute != null) { - return false; - } - } else if (!binaryAttribute.equals(other.binaryAttribute)) { - return false; - } - if (binarySetAttribute == null) { - if (other.binarySetAttribute != null) { - return false; - } - } else if (!binarySetAttribute.equals(other.binarySetAttribute)) { - return false; - } - return true; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/BinaryAttributeByteBufferClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/BinaryAttributeByteBufferClass.java deleted file mode 100644 index 4f4433fd6c37..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/BinaryAttributeByteBufferClass.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.nio.ByteBuffer; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Test domain class with byteBuffer attribute, byteBuffer set and a string key - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class BinaryAttributeByteBufferClass { - - private String key; - private ByteBuffer binaryAttribute; - private Set binarySetAttribute; - - @DynamoDbHashKey(attributeName = "key") - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute(attributeName = "binaryAttribute") - public ByteBuffer getBinaryAttribute() { - return binaryAttribute; - } - - public void setBinaryAttribute(ByteBuffer binaryAttribute) { - this.binaryAttribute = binaryAttribute; - } - - @DynamoDbAttribute(attributeName = "binarySetAttribute") - public Set getBinarySetAttribute() { - return binarySetAttribute; - } - - public void setBinarySetAttribute(Set binarySetAttribute) { - this.binarySetAttribute = binarySetAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((binaryAttribute == null) ? 0 : binaryAttribute.hashCode()); - result = prime * result + ((binarySetAttribute == null) ? 0 : binarySetAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - BinaryAttributeByteBufferClass other = (BinaryAttributeByteBufferClass) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (binaryAttribute == null) { - if (other.binaryAttribute != null) { - return false; - } - } else if (!binaryAttribute.equals(other.binaryAttribute)) { - return false; - } - if (binarySetAttribute == null) { - if (other.binarySetAttribute != null) { - return false; - } - } else if (!binarySetAttribute.equals(other.binarySetAttribute)) { - return false; - } - return true; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/CrossSdkVerificationClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/CrossSdkVerificationClass.java deleted file mode 100644 index 8f52e8af1289..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/CrossSdkVerificationClass.java +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbVersionAttribute; - -/** - * Exhaustive exercise of DynamoDB domain mapping, exercising every supported - * data type. - */ -@DynamoDbTable(tableName = "aws-xsdk") -public class CrossSdkVerificationClass { - - private String key; - private String rangeKey; - private Long version; - private String lastUpdater; - - private Integer integerAttribute; - private Long longAttribute; - private Double doubleAttribute; - private Float floatAttribute; - private BigDecimal bigDecimalAttribute; - private BigInteger bigIntegerAttribute; - private Byte byteAttribute; - private Date dateAttribute; - private Calendar calendarAttribute; - private Boolean booleanAttribute; - - private Set stringSetAttribute; - private Set integerSetAttribute; - private Set doubleSetAttribute; - private Set floatSetAttribute; - private Set bigDecimalSetAttribute; - private Set bigIntegerSetAttribute; - private Set longSetAttribute; - private Set byteSetAttribute; - private Set dateSetAttribute; - private Set calendarSetAttribute; - - // these are kind of pointless, but here for completeness - private Set booleanSetAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbRangeKey - public String getRangeKey() { - return rangeKey; - } - - public void setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - public String lastUpdater() { - return lastUpdater; - } - - public void setLastUpdater(String lastUpdater) { - this.lastUpdater = lastUpdater; - } - - public Integer getIntegerAttribute() { - return integerAttribute; - } - - public void setIntegerAttribute(Integer integerAttribute) { - this.integerAttribute = integerAttribute; - } - - public Long longAttribute() { - return longAttribute; - } - - public void setLongAttribute(Long longAttribute) { - this.longAttribute = longAttribute; - } - - public Double getDoubleAttribute() { - return doubleAttribute; - } - - public void setDoubleAttribute(Double doubleAttribute) { - this.doubleAttribute = doubleAttribute; - } - - public Float getFloatAttribute() { - return floatAttribute; - } - - public void setFloatAttribute(Float floatAttribute) { - this.floatAttribute = floatAttribute; - } - - public BigDecimal bigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - public BigInteger bigIntegerAttribute() { - return bigIntegerAttribute; - } - - public void setBigIntegerAttribute(BigInteger bigIntegerAttribute) { - this.bigIntegerAttribute = bigIntegerAttribute; - } - - public Byte byteAttribute() { - return byteAttribute; - } - - public void setByteAttribute(Byte byteAttribute) { - this.byteAttribute = byteAttribute; - } - - public Date getDateAttribute() { - return dateAttribute; - } - - public void setDateAttribute(Date dateAttribute) { - this.dateAttribute = dateAttribute; - } - - public Calendar getCalendarAttribute() { - return calendarAttribute; - } - - public void setCalendarAttribute(Calendar calendarAttribute) { - this.calendarAttribute = calendarAttribute; - } - - public Boolean booleanAttribute() { - return booleanAttribute; - } - - public void setBooleanAttribute(Boolean booleanAttribute) { - this.booleanAttribute = booleanAttribute; - } - - public Set getIntegerSetAttribute() { - return integerSetAttribute; - } - - public void setIntegerSetAttribute(Set integerSetAttribute) { - this.integerSetAttribute = integerSetAttribute; - } - - public Set getDoubleSetAttribute() { - return doubleSetAttribute; - } - - public void setDoubleSetAttribute(Set doubleSetAttribute) { - this.doubleSetAttribute = doubleSetAttribute; - } - - public Set getFloatSetAttribute() { - return floatSetAttribute; - } - - public void setFloatSetAttribute(Set floatSetAttribute) { - this.floatSetAttribute = floatSetAttribute; - } - - public Set bigDecimalSetAttribute() { - return bigDecimalSetAttribute; - } - - public void setBigDecimalSetAttribute(Set bigDecimalSetAttribute) { - this.bigDecimalSetAttribute = bigDecimalSetAttribute; - } - - public Set bigIntegerSetAttribute() { - return bigIntegerSetAttribute; - } - - public void setBigIntegerSetAttribute(Set bigIntegerSetAttribute) { - this.bigIntegerSetAttribute = bigIntegerSetAttribute; - } - - public Set longSetAttribute() { - return longSetAttribute; - } - - public void setLongSetAttribute(Set longSetAttribute) { - this.longSetAttribute = longSetAttribute; - } - - public Set byteSetAttribute() { - return byteSetAttribute; - } - - public void setByteSetAttribute(Set byteSetAttribute) { - this.byteSetAttribute = byteSetAttribute; - } - - public Set getDateSetAttribute() { - return dateSetAttribute; - } - - public void setDateSetAttribute(Set dateSetAttribute) { - this.dateSetAttribute = dateSetAttribute; - } - - public Set getCalendarSetAttribute() { - return calendarSetAttribute; - } - - public void setCalendarSetAttribute(Set calendarSetAttribute) { - this.calendarSetAttribute = calendarSetAttribute; - } - - public Set booleanSetAttribute() { - return booleanSetAttribute; - } - - public void setBooleanSetAttribute(Set booleanSetAttribute) { - this.booleanSetAttribute = booleanSetAttribute; - } - - public Set stringSetAttribute() { - return stringSetAttribute; - } - - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((bigDecimalSetAttribute == null) ? 0 : bigDecimalSetAttribute.hashCode()); - result = prime * result + ((bigIntegerAttribute == null) ? 0 : bigIntegerAttribute.hashCode()); - result = prime * result + ((bigIntegerSetAttribute == null) ? 0 : bigIntegerSetAttribute.hashCode()); - result = prime * result + ((booleanAttribute == null) ? 0 : booleanAttribute.hashCode()); - result = prime * result + ((booleanSetAttribute == null) ? 0 : booleanSetAttribute.hashCode()); - result = prime * result + ((byteAttribute == null) ? 0 : byteAttribute.hashCode()); - result = prime * result + ((byteSetAttribute == null) ? 0 : byteSetAttribute.hashCode()); - result = prime * result + ((calendarAttribute == null) ? 0 : calendarAttribute.hashCode()); - result = prime * result + ((calendarSetAttribute == null) ? 0 : calendarSetAttribute.hashCode()); - result = prime * result + ((dateAttribute == null) ? 0 : dateAttribute.hashCode()); - result = prime * result + ((dateSetAttribute == null) ? 0 : dateSetAttribute.hashCode()); - result = prime * result + ((doubleAttribute == null) ? 0 : doubleAttribute.hashCode()); - result = prime * result + ((doubleSetAttribute == null) ? 0 : doubleSetAttribute.hashCode()); - result = prime * result + ((floatAttribute == null) ? 0 : floatAttribute.hashCode()); - result = prime * result + ((floatSetAttribute == null) ? 0 : floatSetAttribute.hashCode()); - result = prime * result + ((integerAttribute == null) ? 0 : integerAttribute.hashCode()); - result = prime * result + ((integerSetAttribute == null) ? 0 : integerSetAttribute.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((lastUpdater == null) ? 0 : lastUpdater.hashCode()); - result = prime * result + ((longAttribute == null) ? 0 : longAttribute.hashCode()); - result = prime * result + ((longSetAttribute == null) ? 0 : longSetAttribute.hashCode()); - result = prime * result + ((rangeKey == null) ? 0 : rangeKey.hashCode()); - result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - CrossSdkVerificationClass other = (CrossSdkVerificationClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) { - return false; - } - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) { - return false; - } - if (bigDecimalSetAttribute == null) { - if (other.bigDecimalSetAttribute != null) { - return false; - } - } else if (!bigDecimalSetAttribute.equals(other.bigDecimalSetAttribute)) { - return false; - } - if (bigIntegerAttribute == null) { - if (other.bigIntegerAttribute != null) { - return false; - } - } else if (!bigIntegerAttribute.equals(other.bigIntegerAttribute)) { - return false; - } - if (bigIntegerSetAttribute == null) { - if (other.bigIntegerSetAttribute != null) { - return false; - } - } else if (!bigIntegerSetAttribute.equals(other.bigIntegerSetAttribute)) { - return false; - } - if (booleanAttribute == null) { - if (other.booleanAttribute != null) { - return false; - } - } else if (!booleanAttribute.equals(other.booleanAttribute)) { - return false; - } - if (booleanSetAttribute == null) { - if (other.booleanSetAttribute != null) { - return false; - } - } else if (!booleanSetAttribute.equals(other.booleanSetAttribute)) { - return false; - } - if (byteAttribute == null) { - if (other.byteAttribute != null) { - return false; - } - } else if (!byteAttribute.equals(other.byteAttribute)) { - return false; - } - if (byteSetAttribute == null) { - if (other.byteSetAttribute != null) { - return false; - } - } else if (!byteSetAttribute.equals(other.byteSetAttribute)) { - return false; - } - if (calendarAttribute == null) { - if (other.calendarAttribute != null) { - return false; - } - } else if (!calendarAttribute.equals(other.calendarAttribute)) { - return false; - } - if (calendarSetAttribute == null) { - if (other.calendarSetAttribute != null) { - return false; - } - } else if (!calendarSetAttribute.equals(other.calendarSetAttribute)) { - return false; - } - if (dateAttribute == null) { - if (other.dateAttribute != null) { - return false; - } - } else if (!dateAttribute.equals(other.dateAttribute)) { - return false; - } - if (dateSetAttribute == null) { - if (other.dateSetAttribute != null) { - return false; - } - } else if (!dateSetAttribute.equals(other.dateSetAttribute)) { - return false; - } - if (doubleAttribute == null) { - if (other.doubleAttribute != null) { - return false; - } - } else if (!doubleAttribute.equals(other.doubleAttribute)) { - return false; - } - if (doubleSetAttribute == null) { - if (other.doubleSetAttribute != null) { - return false; - } - } else if (!doubleSetAttribute.equals(other.doubleSetAttribute)) { - return false; - } - if (floatAttribute == null) { - if (other.floatAttribute != null) { - return false; - } - } else if (!floatAttribute.equals(other.floatAttribute)) { - return false; - } - if (floatSetAttribute == null) { - if (other.floatSetAttribute != null) { - return false; - } - } else if (!floatSetAttribute.equals(other.floatSetAttribute)) { - return false; - } - if (integerAttribute == null) { - if (other.integerAttribute != null) { - return false; - } - } else if (!integerAttribute.equals(other.integerAttribute)) { - return false; - } - if (integerSetAttribute == null) { - if (other.integerSetAttribute != null) { - return false; - } - } else if (!integerSetAttribute.equals(other.integerSetAttribute)) { - return false; - } - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (lastUpdater == null) { - if (other.lastUpdater != null) { - return false; - } - } else if (!lastUpdater.equals(other.lastUpdater)) { - return false; - } - if (longAttribute == null) { - if (other.longAttribute != null) { - return false; - } - } else if (!longAttribute.equals(other.longAttribute)) { - return false; - } - if (longSetAttribute == null) { - if (other.longSetAttribute != null) { - return false; - } - } else if (!longSetAttribute.equals(other.longSetAttribute)) { - return false; - } - if (rangeKey == null) { - if (other.rangeKey != null) { - return false; - } - } else if (!rangeKey.equals(other.rangeKey)) { - return false; - } - if (stringSetAttribute == null) { - if (other.stringSetAttribute != null) { - return false; - } - } else if (!stringSetAttribute.equals(other.stringSetAttribute)) { - return false; - } - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/Currency.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/Currency.java deleted file mode 100644 index 8b5936b6f6c5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/Currency.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -/** - * A currency object. - */ -public class Currency { - private KeyAndVal kav; - - public Currency(final Double amount, final String unit) { - this.kav = new KeyAndVal(amount, unit); - } - - public Currency() { - this((Double) null, (String) null); - } - - public Double getAmount() { - return kav.getKey(); - } - - public void setAmount(final Double amount) { - kav.setKey(amount); - } - - public String getUnit() { - return kav.getVal(); - } - - public void setUnit(final String unit) { - kav.setVal(unit); - } - - @Override - public final boolean equals(final Object o) { - return (o == this || (o instanceof Currency && kav.equals(((Currency) o).kav))); - } - - @Override - public final int hashCode() { - return kav.hashCode(); - } - - @Override - public final String toString() { - return kav.toString(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/DateRange.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/DateRange.java deleted file mode 100644 index b40269c8021d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/DateRange.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.util.Date; - -/** - * A date range object. - */ -public class DateRange { - private KeyAndVal kav; - - public DateRange(final Date start, final Date end) { - this.kav = new KeyAndVal(start, end); - } - - public DateRange(final Date date, final Long start, final Long end) { - this(start == null ? null : new Date(date.getTime() + start), end == null ? null : new Date(date.getTime() + end)); - } - - public DateRange() { - this((Date) null, (Date) null); - } - - public Date getStart() { - return kav.getKey(); - } - - public void setStart(final Date start) { - kav.setKey(start); - } - - public Date getEnd() { - return kav.getVal(); - } - - public void setEnd(final Date end) { - kav.setVal(end); - } - - @Override - public final boolean equals(final Object o) { - return (o == this || (o instanceof DateRange && kav.equals(((DateRange) o).kav))); - } - - @Override - public final int hashCode() { - return kav.hashCode(); - } - - @Override - public final String toString() { - return kav.toString(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/GsiWithAlwaysUpdateTimestamp.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/GsiWithAlwaysUpdateTimestamp.java deleted file mode 100644 index 84f1179cd02a..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/GsiWithAlwaysUpdateTimestamp.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGenerateStrategy; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAutoGeneratedTimestamp; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbIndexHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -@DynamoDbTable(tableName = "Foo") -public class GsiWithAlwaysUpdateTimestamp { - - @DynamoDbHashKey - private String hashKey; - - @DynamoDbRangeKey - private String rangeKey; - - @DynamoDbAutoGeneratedTimestamp(strategy = DynamoDbAutoGenerateStrategy.ALWAYS) - @DynamoDbIndexHashKey(globalSecondaryIndexName = "last-mod-date") - private Long lastModifiedDate; - - public String getHashKey() { - return hashKey; - } - - public GsiWithAlwaysUpdateTimestamp setHashKey(String hashKey) { - this.hashKey = hashKey; - return this; - } - - public String getRangeKey() { - return rangeKey; - } - - public GsiWithAlwaysUpdateTimestamp setRangeKey(String rangeKey) { - this.rangeKey = rangeKey; - return this; - } - - public Long getLastModifiedDate() { - return lastModifiedDate; - } - - public GsiWithAlwaysUpdateTimestamp setLastModifiedDate(Long lastModifiedDate) { - this.lastModifiedDate = lastModifiedDate; - return this; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/KeyAndVal.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/KeyAndVal.java deleted file mode 100644 index fed9940c8b08..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/KeyAndVal.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -/** - * Simple key-val domain class. - */ -public class KeyAndVal { - private K key; - private V val; - - public KeyAndVal(final K key, final V val) { - this.key = key; - this.val = val; - } - - public KeyAndVal() { - this((K) null, (V) null); - } - - public K getKey() { - return this.key; - } - - public void setKey(final K key) { - this.key = key; - } - - public V getVal() { - return this.val; - } - - public void setVal(final V val) { - this.val = val; - } - - public final boolean keyEquals(final Object o) { - return key == o || (key != null && key.equals(o)); - } - - public final boolean valEquals(final Object o) { - return val == o || (val != null && val.equals(o)); - } - - @Override - public final boolean equals(final Object o) { - return o == this || (o instanceof KeyAndVal && keyEquals(((KeyAndVal) o).key) && valEquals(((KeyAndVal) o).val)); - } - - @Override - public final int hashCode() { - return 1 + (key == null ? 0 : key.hashCode()) + (val == null ? 0 : val.hashCode()); - } - - @Override - public final String toString() { - return "[" + key + "," + val + "]"; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/PhoneNumber.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/PhoneNumber.java deleted file mode 100644 index 5c023a9f84bf..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/PhoneNumber.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -/** - * A phone number object. - */ -public class PhoneNumber { - - private String areaCode; - private String exchange; - private String subscriber; - - public PhoneNumber(final String areaCode, final String exchange, final String subscriber) { - setAreaCode(areaCode); - setExchange(exchange); - setSubscriber(subscriber); - } - - public PhoneNumber() { - this(null, null, null); - } - - public String getAreaCode() { - return this.areaCode; - } - - public void setAreaCode(final String areaCode) { - this.areaCode = areaCode; - } - - public String getExchange() { - return this.exchange; - } - - public void setExchange(final String exchange) { - this.exchange = exchange; - } - - public String getSubscriber() { - return this.subscriber; - } - - public void setSubscriber(String subscriber) { - this.subscriber = subscriber; - } - - public final boolean areaCodeEquals(final Object o) { - return (areaCode == o || (areaCode != null && areaCode.equals(o))); - } - - public final boolean exchangeEquals(final Object o) { - return (exchange == o || (exchange != null && exchange.equals(o))); - } - - public final boolean subscriberEquals(final Object o) { - return (subscriber == o || (subscriber != null && subscriber.equals(o))); - } - - @Override - public final boolean equals(final Object o) { - return (o == this || (o instanceof PhoneNumber && areaCodeEquals(((PhoneNumber) o).areaCode) && exchangeEquals(((PhoneNumber) o).exchange) && subscriberEquals(((PhoneNumber) o).subscriber))); - } - - @Override - public final int hashCode() { - return (1 + (areaCode == null ? 0 : areaCode.hashCode()) + (exchange == null ? 0 : exchange.hashCode()) + (subscriber == null ? 0 : subscriber.hashCode())); - } - - @Override - public final String toString() { - return (getClass().getName() + "{areaCode=" + areaCode + ",exchange=" + exchange + ",subscriber=" + subscriber + "}"); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/RangeKeyClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/RangeKeyClass.java deleted file mode 100644 index fdeb5940d0a3..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/RangeKeyClass.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.math.BigDecimal; -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbRangeKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbVersionAttribute; - -/** - * Comprehensive domain class - */ -@DynamoDbTable(tableName = "aws-java-sdk-range-test") -public class RangeKeyClass { - - private long key; - private double rangeKey; - private Long version; - - private Set integerSetAttribute; - private Set stringSetAttribute; - private BigDecimal bigDecimalAttribute; - private String stringAttribute; - - @DynamoDbHashKey - public long getKey() { - return key; - } - - public void setKey(long key) { - this.key = key; - } - - @DynamoDbRangeKey - public double getRangeKey() { - return rangeKey; - } - - public void setRangeKey(double rangeKey) { - this.rangeKey = rangeKey; - } - - @DynamoDbAttribute(attributeName = "integerSetAttribute") - public Set getIntegerAttribute() { - return integerSetAttribute; - } - - public void setIntegerAttribute(Set integerAttribute) { - this.integerSetAttribute = integerAttribute; - } - - @DynamoDbAttribute - public Set getStringSetAttribute() { - return stringSetAttribute; - } - - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } - - @DynamoDbAttribute - public BigDecimal getBigDecimalAttribute() { - return bigDecimalAttribute; - } - - public void setBigDecimalAttribute(BigDecimal bigDecimalAttribute) { - this.bigDecimalAttribute = bigDecimalAttribute; - } - - @DynamoDbAttribute - public String getStringAttribute() { - return stringAttribute; - } - - public void setStringAttribute(String stringAttribute) { - this.stringAttribute = stringAttribute; - } - - @DynamoDbVersionAttribute - public Long getVersion() { - return version; - } - - public void setVersion(Long version) { - this.version = version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((bigDecimalAttribute == null) ? 0 : bigDecimalAttribute.hashCode()); - result = prime * result + ((integerSetAttribute == null) ? 0 : integerSetAttribute.hashCode()); - result = prime * result + (int) (key ^ (key >>> 32)); - long temp; - temp = Double.doubleToLongBits(rangeKey); - result = prime * result + (int) (temp ^ (temp >>> 32)); - result = prime * result + ((stringAttribute == null) ? 0 : stringAttribute.hashCode()); - result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - RangeKeyClass other = (RangeKeyClass) obj; - if (bigDecimalAttribute == null) { - if (other.bigDecimalAttribute != null) { - return false; - } - } else if (!bigDecimalAttribute.equals(other.bigDecimalAttribute)) { - return false; - } - if (integerSetAttribute == null) { - if (other.integerSetAttribute != null) { - return false; - } - } else if (!integerSetAttribute.equals(other.integerSetAttribute)) { - return false; - } - if (key != other.key) { - return false; - } - if (Double.doubleToLongBits(rangeKey) != Double.doubleToLongBits(other.rangeKey)) { - return false; - } - if (stringAttribute == null) { - if (other.stringAttribute != null) { - return false; - } - } else if (!stringAttribute.equals(other.stringAttribute)) { - return false; - } - if (stringSetAttribute == null) { - if (other.stringSetAttribute != null) { - return false; - } - } else if (!stringSetAttribute.equals(other.stringSetAttribute)) { - return false; - } - if (version == null) { - if (other.version != null) { - return false; - } - } else if (!version.equals(other.version)) { - return false; - } - return true; - } - - @Override - public String toString() { - return "RangeKeyClass [key=" + key + ", rangeKey=" + rangeKey + ", version=" + version - + ", integerSetAttribute=" + integerSetAttribute + ", stringSetAttribute=" + stringSetAttribute - + ", bigDecimalAttribute=" + bigDecimalAttribute + ", stringAttribute=" + stringAttribute + "]"; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/S3LinksTestClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/S3LinksTestClass.java deleted file mode 100644 index 11ef37ee2eb9..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/S3LinksTestClass.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.S3Link; - -/** - * Test domain class with a single string key, and two S3Links - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class S3LinksTestClass { - - private String key; - private S3Link s3LinkWest; - private S3Link s3LinkEast; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public S3LinksTestClass withKey(String key) { - setKey(key); - return this; - } - - public S3Link s3LinkWest() { - return s3LinkWest; - } - - public void setS3LinkWest(S3Link s3LinkAttribute) { - this.s3LinkWest = s3LinkAttribute; - } - - public S3Link s3LinkEast() { - return s3LinkEast; - } - - public void setS3LinkEast(S3Link s3LinkEast) { - this.s3LinkEast = s3LinkEast; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/StringAttributeClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/StringAttributeClass.java deleted file mode 100644 index 9df6e653868d..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/StringAttributeClass.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Test domain class with a single string attribute and a string key - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class StringAttributeClass { - - private String key; - private String stringAttribute; - private String renamedAttribute; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute - public String getStringAttribute() { - return stringAttribute; - } - - public void setStringAttribute(String stringAttribute) { - this.stringAttribute = stringAttribute; - } - - @DynamoDbAttribute(attributeName = "originalName") - public String getRenamedAttribute() { - return renamedAttribute; - } - - public void setRenamedAttribute(String renamedAttribute) { - this.renamedAttribute = renamedAttribute; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((renamedAttribute == null) ? 0 : renamedAttribute.hashCode()); - result = prime * result + ((stringAttribute == null) ? 0 : stringAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - StringAttributeClass other = (StringAttributeClass) obj; - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (renamedAttribute == null) { - if (other.renamedAttribute != null) { - return false; - } - } else if (!renamedAttribute.equals(other.renamedAttribute)) { - return false; - } - if (stringAttribute == null) { - if (other.stringAttribute != null) { - return false; - } - } else if (!stringAttribute.equals(other.stringAttribute)) { - return false; - } - return true; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/StringSetAttributeClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/StringSetAttributeClass.java deleted file mode 100644 index 716c3512c19e..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/StringSetAttributeClass.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.util.Set; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbAttribute; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; - -/** - * Test domain class with a string set attribute and a string key - */ -@DynamoDbTable(tableName = "aws-java-sdk-util") -public class StringSetAttributeClass { - - private String key; - private Set stringSetAttribute; - private Set StringSetAttributeRenamed; - - @DynamoDbHashKey - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - @DynamoDbAttribute - public Set getStringSetAttribute() { - return stringSetAttribute; - } - - public void setStringSetAttribute(Set stringSetAttribute) { - this.stringSetAttribute = stringSetAttribute; - } - - @DynamoDbAttribute(attributeName = "originalName") - public Set getStringSetAttributeRenamed() { - return StringSetAttributeRenamed; - } - - public void setStringSetAttributeRenamed(Set stringSetAttributeRenamed) { - StringSetAttributeRenamed = stringSetAttributeRenamed; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((StringSetAttributeRenamed == null) ? 0 : StringSetAttributeRenamed.hashCode()); - result = prime * result + ((key == null) ? 0 : key.hashCode()); - result = prime * result + ((stringSetAttribute == null) ? 0 : stringSetAttribute.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - StringSetAttributeClass other = (StringSetAttributeClass) obj; - if (StringSetAttributeRenamed == null) { - if (other.StringSetAttributeRenamed != null) { - return false; - } - } else if (!StringSetAttributeRenamed.equals(other.StringSetAttributeRenamed)) { - return false; - } - if (key == null) { - if (other.key != null) { - return false; - } - } else if (!key.equals(other.key)) { - return false; - } - if (stringSetAttribute == null) { - if (other.stringSetAttribute != null) { - return false; - } - } else if (!stringSetAttribute.equals(other.stringSetAttribute)) { - return false; - } - return true; - } - - -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/SubClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/SubClass.java deleted file mode 100644 index a0eec5bd30fb..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/SubClass.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.nio.ByteBuffer; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbDocument; - -@DynamoDbDocument -public class SubClass { - - private String name; - private Integer value; - private ByteBuffer bb; - - public SubClass() { - name = "name"; - value = 123; - } - - public static boolean equals(T one, T two) { - if (one == null) { - return (two == null); - } else { - return one.equals(two); - } - } - - public String getName() { - return name; - } - - public void setName(String n) { - name = n; - } - - public Integer getValue() { - return value; - } - - public void setValue(Integer i) { - value = i; - } - - public ByteBuffer getNull() { - return bb; - } - - public void setNull(ByteBuffer b) { - bb = b; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof SubClass)) { - return false; - } - - SubClass that = (SubClass) o; - - return equals(this.name, that.name) - && equals(this.value, that.value) - && equals(this.bb, that.bb); - } - - @Override - public String toString() { - return "{name=" + name + ", value=" + value + ", bb=" + bb + "}"; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/TestClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/TestClass.java deleted file mode 100644 index c426da394976..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/TestClass.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbHashKey; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbMarshalling; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbNativeBoolean; -import software.amazon.awssdk.services.dynamodb.datamodeling.DynamoDbTable; -import software.amazon.awssdk.services.dynamodb.datamodeling.RandomUuidMarshaller; -import software.amazon.awssdk.services.dynamodb.datamodeling.S3Link; - -@DynamoDbTable(tableName = "nonexisting-test-tablename") -public class TestClass { - - @DynamoDbHashKey - public String getId() { - return null; - } - - public void setId(String id) { - } - - public boolean getBoolean() { - return false; - } - - public void setBoolean(boolean value) { - } - - public Boolean getBoxedBoolean() { - return false; - } - - public void setBoxedBoolean(Boolean value) { - } - - @DynamoDbNativeBoolean - public boolean getNativeBoolean() { - return false; - } - - public void setNativeBoolean(boolean value) { - } - - public String getString() { - return null; - } - - public void setString(String value) { - } - - public UUID getUuid() { - return null; - } - - public void setUuid(UUID u) { - } - - @DynamoDbMarshalling(marshallerClass = RandomUuidMarshaller.class) - public String getCustomString() { - return null; - } - - public void setCustomString(String s) { - } - - public Date getDate() { - return null; - } - - public void setDate(Date d) { - } - - public Calendar getCalendar() { - return null; - } - - public void setCalendar(Calendar c) { - } - - public byte getByte() { - return 0; - } - - public void setByte(byte b) { - } - - public Byte getBoxedByte() { - return 0; - } - - public void setBoxedByte(Byte b) { - } - - public short getShort() { - return 0; - } - - public void setShort(short s) { - } - - public Short getBoxedShort() { - return 0; - } - - public void setBoxedShort(Short s) { - } - - public int getInt() { - return 0; - } - - public void setInt(int i) { - } - - public Integer getBoxedInt() { - return 0; - } - - public void setBoxedInt(Integer i) { - } - - public long getLong() { - return 0; - } - - public void setLong(long l) { - } - - public Long getBoxedLong() { - return 0l; - } - - public void setBoxedLong(Long l) { - } - - public BigInteger getBigInt() { - return BigInteger.ZERO; - } - - public void setBigInt(BigInteger i) { - } - - public float getFloat() { - return 0; - } - - public void setFloat(float f) { - } - - public Float getBoxedFloat() { - return 0f; - } - - public void setBoxedFloat(Float f) { - } - - public double getDouble() { - return 0; - } - - public void setDouble(double d) { - } - - public Double getBoxedDouble() { - return 0d; - } - - public void setBoxedDouble(Double d) { - } - - public BigDecimal getBigDecimal() { - return BigDecimal.ZERO; - } - - public void setBigDecimal(BigDecimal d) { - } - - public byte[] getByteArray() { - return null; - } - - public void setByteArray(byte[] b) { - } - - public ByteBuffer getByteBuffer() { - return null; - } - - public void setByteBuffer(ByteBuffer b) { - } - - public Set getBooleanSet() { - return null; - } - - public void setBooleanSet(Set s) { - } - - public Set getUuidSet() { - return null; - } - - public void setUuidSet(Set s) { - } - - public Set getStringSet() { - return null; - } - - public void setStringSet(Set s) { - } - - public Set getDateSet() { - return null; - } - - public void setDateSet(Set d) { - } - - public Set getCalendarSet() { - return null; - } - - public void setCalendarSet(Set c) { - } - - public Set getByteSet() { - return null; - } - - public void setByteSet(Set s) { - } - - public Set getShortSet() { - return null; - } - - public void setShortSet(Set s) { - } - - public Set getIntSet() { - return null; - } - - public void setIntSet(Set s) { - } - - public Set getLongSet() { - return null; - } - - public void setLongSet(Set s) { - } - - public Set getBigIntegerSet() { - return null; - } - - public void setBigIntegerSet(Set s) { - } - - public Set getFloatSet() { - return null; - } - - public void setFloatSet(Set s) { - } - - public Set getDoubleSet() { - return null; - } - - public void setDoubleSet(Set s) { - } - - public Set getBigDecimalSet() { - return null; - } - - public void setBigDecimalSet(Set s) { - } - - public Set getByteArraySet() { - return null; - } - - public void setByteArraySet(Set s) { - } - - public Set getByteBufferSet() { - return null; - } - - public void setByteBufferSet(Set s) { - } - - public Set getObjectSet() { - return null; - } - - public void setObjectSet(Set s) { - } - - public List getList() { - return null; - } - - public void setList(List l) { - } - - public List getObjectList() { - return null; - } - - public void setObjectList(List l) { - } - - public List> getSetList() { - return null; - } - - public void setSetList(List> l) { - } - - public Map getMap() { - return null; - } - - public void setMap(Map m) { - } - - public Map> getSetMap() { - return null; - } - - public void setSetMap(Map> m) { - } - - public Map getBogusMap() { - return null; - } - - public void setBogusMap(Map m) { - } - - public SubClass getObject() { - return null; - } - - public void setObject(SubClass c) { - } - - public S3Link getS3Link() { - return null; - } - - public void setS3Link(S3Link link) { - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/UnannotatedSubClass.java b/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/UnannotatedSubClass.java deleted file mode 100644 index bb6247319954..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/software/amazon/awssdk/services/dynamodb/pojos/UnannotatedSubClass.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.services.dynamodb.pojos; - -public class UnannotatedSubClass { - public UnannotatedSubClass getChild() { - return null; - } - - public void setChild(UnannotatedSubClass c) { - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/RequiredResources.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/RequiredResources.java deleted file mode 100644 index 71d3f3f6434f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/RequiredResources.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Annotation for resources required for the test case. It could be applied to - * either a type (test class) or a method (test method). - */ -@Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.METHOD, ElementType.TYPE}) -public @interface RequiredResources { - - /** - * An array of RequiredResource annotations - */ - RequiredResource[] value() default {}; - - enum ResourceCreationPolicy { - /** - * Existing resource will be reused if it matches the required resource - * definition (i.e. TestResource.getResourceStatus() returns AVAILABLE). - */ - REUSE_EXISTING, - /** - * Always destroy existing resources (if any) and then recreate new ones for test. - */ - ALWAYS_RECREATE; - } - - enum ResourceRetentionPolicy { - /** - * Do not delete the created resource after test. - */ - KEEP, - /** - * When used for @RequiredAnnota - */ - DESTROY_IMMEDIATELY, - DESTROY_AFTER_ALL_TESTS; - } - - @interface RequiredResource { - - /** - * The Class object of the TestResource class - */ - Class resource(); - - /** - * How the resource should be created before the test starts. - */ - ResourceCreationPolicy creationPolicy(); - - /** - * Retention policy after the test is done. - */ - ResourceRetentionPolicy retentionPolicy(); - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/ResourceCentricBlockJUnit4ClassRunner.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/ResourceCentricBlockJUnit4ClassRunner.java deleted file mode 100644 index 0cb576b15a18..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/ResourceCentricBlockJUnit4ClassRunner.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources; - -import java.util.HashSet; -import java.util.Set; -import org.junit.Ignore; -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import utils.resources.RequiredResources.RequiredResource; -import utils.resources.RequiredResources.ResourceRetentionPolicy; - -public class ResourceCentricBlockJUnit4ClassRunner extends BlockJUnit4ClassRunner { - - private final Set resourcesToBeDestroyedAfterAllTests; - - private final RequiredResources classRequiredResourcesAnnotation; - - private final Logger log = LoggerFactory.getLogger(ResourceCentricBlockJUnit4ClassRunner.class); - - public ResourceCentricBlockJUnit4ClassRunner(Class klass) - throws InitializationError { - super(klass); - - classRequiredResourcesAnnotation = klass.getAnnotation(RequiredResources.class); - resourcesToBeDestroyedAfterAllTests = new HashSet(); - } - - /** - * - */ - private static TestResource createResourceInstance(RequiredResource resourceAnnotation) - throws InstantiationException, IllegalAccessException { - Class resourceClazz = resourceAnnotation.resource(); - if (resourceClazz == null) { - throw new IllegalArgumentException( - "resource parameter is missing for the @RequiredResource annotation."); - } - return resourceClazz.newInstance(); - } - - @Override - protected void runChild(final FrameworkMethod method, RunNotifier notifier) { - Description description = describeChild(method); - if (method.getAnnotation(Ignore.class) != null) { - notifier.fireTestIgnored(description); - } else { - RequiredResources annotation = method.getAnnotation(RequiredResources.class); - if (annotation != null) { - try { - beforeRunLeaf(annotation.value()); - } catch (Exception e) { - notifier.fireTestFailure(new Failure(description, e)); - } - - } - - runLeaf(methodBlock(method), description, notifier); - - if (annotation != null) { - try { - afterRunLeaf(annotation.value()); - } catch (Exception e) { - notifier.fireTestFailure(new Failure(description, e)); - } - } - } - } - - /** - * Override the withBeforeClasses method to inject executing resource - * creation between @BeforeClass methods and test methods. - */ - @Override - protected Statement withBeforeClasses(final Statement statement) { - Statement withRequiredResourcesCreation = new Statement() { - - @Override - public void evaluate() throws Throwable { - if (classRequiredResourcesAnnotation != null) { - beforeRunClass(classRequiredResourcesAnnotation.value()); - } - statement.evaluate(); - } - }; - return super.withBeforeClasses(withRequiredResourcesCreation); - } - - /** - * Override the withAfterClasses method to inject executing resource - * creation between test methods and the @AfterClass methods. - */ - @Override - protected Statement withAfterClasses(final Statement statement) { - Statement withRequiredResourcesDeletion = new Statement() { - - @Override - public void evaluate() throws Throwable { - statement.evaluate(); - afterRunClass(); - } - }; - return super.withAfterClasses(withRequiredResourcesDeletion); - } - - private void beforeRunClass(RequiredResource[] resourcesAnnotation) - throws InstantiationException, IllegalAccessException, InterruptedException { - log.debug("Processing @RequiredResources before running the test class..."); - for (RequiredResource resourceAnnotation : resourcesAnnotation) { - TestResource resource = createResourceInstance(resourceAnnotation); - TestResourceUtils.createResource(resource, resourceAnnotation.creationPolicy()); - - if (resourceAnnotation.retentionPolicy() != ResourceRetentionPolicy.KEEP) { - resourcesToBeDestroyedAfterAllTests.add(resource); - } - } - } - - private void afterRunClass() - throws InstantiationException, IllegalAccessException, InterruptedException { - log.debug("Processing @RequiredResources after running the test class..."); - for (TestResource resource : resourcesToBeDestroyedAfterAllTests) { - TestResourceUtils.deleteResource(resource); - } - } - - private void beforeRunLeaf(RequiredResource[] resourcesAnnotation) - throws InstantiationException, IllegalAccessException, InterruptedException { - log.debug("Processing @RequiredResources before running the test..."); - for (RequiredResource resourceAnnotation : resourcesAnnotation) { - TestResource resource = createResourceInstance(resourceAnnotation); - TestResourceUtils.createResource(resource, resourceAnnotation.creationPolicy()); - - if (resourceAnnotation.retentionPolicy() == ResourceRetentionPolicy.DESTROY_AFTER_ALL_TESTS) { - resourcesToBeDestroyedAfterAllTests.add(resource); - } - } - } - - private void afterRunLeaf(RequiredResource[] resourcesAnnotation) - throws InstantiationException, IllegalAccessException, InterruptedException { - log.debug("Processing @RequiredResources after running the test..."); - for (RequiredResource resourceAnnotation : resourcesAnnotation) { - TestResource resource = createResourceInstance(resourceAnnotation); - - if (resourceAnnotation.retentionPolicy() == ResourceRetentionPolicy.DESTROY_IMMEDIATELY) { - TestResourceUtils.deleteResource(resource); - } - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/TestResource.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/TestResource.java deleted file mode 100644 index 98736d15a401..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/TestResource.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources; - -/** - * An interface which represents a resource to be used in a test case. - *

    - * Note that sub-classes implementing this interface must provide a no-arg - * constructor. - */ -public interface TestResource { - - /** - * Create/initialize the resource which this TestResource represents. - * - * @param waitTillFinished Whether this method should block until the resource is fully - * initialized. - */ - void create(boolean waitTillFinished); - - /** - * Delete the resource which this TestResource represents. - * - * @param waitTillFinished Whether this method should block until the resource is fully - * initialized. - */ - void delete(boolean waitTillFinished); - - /** - * Returns the current status of the resource which this TestResource - * represents. - */ - ResourceStatus getResourceStatus(); - - /** - * Enum of all the generalized resource statuses. - */ - enum ResourceStatus { - /** - * The resource is currently available, and it is compatible with the - * required resource. - */ - AVAILABLE, - /** - * The resource does not exist and there is no existing resource that is - * incompatible. - */ - NOT_EXIST, - /** - * There is an existing resource that has to be removed before creating - * the required resource. For example, DDB table with the same name but - * different table schema. - */ - EXIST_INCOMPATIBLE_RESOURCE, - /** - * The resource is in transient state (e.g. creating/deleting/updating) - */ - TRANSIENT, - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/TestResourceUtils.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/TestResourceUtils.java deleted file mode 100644 index 5f11cb7244c5..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/TestResourceUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources; - -import software.amazon.awssdk.testutils.Waiter; -import software.amazon.awssdk.utils.Logger; -import utils.resources.RequiredResources.ResourceCreationPolicy; -import utils.resources.TestResource.ResourceStatus; - - -public class TestResourceUtils { - private static final Logger log = Logger.loggerFor(TestResourceUtils.class); - - public static void createResource(TestResource resource, ResourceCreationPolicy policy) throws InterruptedException { - TestResource.ResourceStatus finalizedStatus = waitForFinalizedStatus(resource); - if (policy == ResourceCreationPolicy.ALWAYS_RECREATE) { - if (finalizedStatus != ResourceStatus.NOT_EXIST) { - resource.delete(true); - } - resource.create(true); - } else if (policy == ResourceCreationPolicy.REUSE_EXISTING) { - switch (finalizedStatus) { - case AVAILABLE: - log.info(() -> "Found existing resource " + resource + " that could be reused..."); - return; - case EXIST_INCOMPATIBLE_RESOURCE: - resource.delete(true); - resource.create(true); - // fallthru - case NOT_EXIST: - resource.create(true); - break; - default: - break; - } - } - } - - public static void deleteResource(TestResource resource) throws InterruptedException { - ResourceStatus finalizedStatus = waitForFinalizedStatus(resource); - if (finalizedStatus != ResourceStatus.NOT_EXIST) { - resource.delete(false); - } - } - - public static ResourceStatus waitForFinalizedStatus(TestResource resource) throws InterruptedException { - return Waiter.run(resource::getResourceStatus) - .until(status -> status != ResourceStatus.TRANSIENT) - .orFail(); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/BasicTempTable.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/BasicTempTable.java deleted file mode 100644 index 979df80e14a2..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/BasicTempTable.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources.tables; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import utils.test.resources.DynamoDBTableResource; -import utils.test.util.DynamoDBTestBase; - -public class BasicTempTable extends DynamoDBTableResource { - - public static final String TEMP_TABLE_NAME = "java-sdk-" + System.currentTimeMillis(); - public static final String HASH_KEY_NAME = "hash"; - public static final Long READ_CAPACITY = 10L; - public static final Long WRITE_CAPACITY = 5L; - public static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = - ProvisionedThroughput.builder().readCapacityUnits(READ_CAPACITY).writeCapacityUnits(WRITE_CAPACITY).build(); - - @Override - protected DynamoDbClient getClient() { - return DynamoDBTestBase.getClient(); - } - - @Override - protected CreateTableRequest getCreateTableRequest() { - CreateTableRequest request = CreateTableRequest.builder() - .tableName(TEMP_TABLE_NAME) - .keySchema( - KeySchemaElement.builder().attributeName(HASH_KEY_NAME) - .keyType(KeyType.HASH).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName( - HASH_KEY_NAME).attributeType( - ScalarAttributeType.S).build()) - .provisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT).build(); - return request; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/BasicTempTableWithLowThroughput.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/BasicTempTableWithLowThroughput.java deleted file mode 100644 index e5bfbe2ee97f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/BasicTempTableWithLowThroughput.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources.tables; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import utils.test.resources.DynamoDBTableResource; -import utils.test.util.DynamoDBTestBase; - -/** - * DynamoDB table used by {@link ProvisionedThroughputThrottlingIntegrationTest} - */ -public class BasicTempTableWithLowThroughput extends DynamoDBTableResource { - - public static final String TEMP_TABLE_NAME = "java-sdk-low-throughput-" + System.currentTimeMillis(); - public static final String HASH_KEY_NAME = "hash"; - public static final Long READ_CAPACITY = 1L; - public static final Long WRITE_CAPACITY = 1L; - public static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = - ProvisionedThroughput.builder().readCapacityUnits(READ_CAPACITY).writeCapacityUnits(WRITE_CAPACITY).build(); - - @Override - protected DynamoDbClient getClient() { - return DynamoDBTestBase.getClient(); - } - - @Override - protected CreateTableRequest getCreateTableRequest() { - CreateTableRequest request = CreateTableRequest.builder() - .tableName(TEMP_TABLE_NAME) - .keySchema( - KeySchemaElement.builder().attributeName(HASH_KEY_NAME) - .keyType(KeyType.HASH).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName( - HASH_KEY_NAME).attributeType( - ScalarAttributeType.S).build()) - .provisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT) - .build(); - return request; - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TempTableWithBinaryKey.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TempTableWithBinaryKey.java deleted file mode 100644 index 1e1c38d58629..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TempTableWithBinaryKey.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources.tables; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import utils.test.resources.DynamoDBTableResource; -import utils.test.util.DynamoDBTestBase; - -public class TempTableWithBinaryKey extends DynamoDBTableResource { - - public static final String TEMP_BINARY_TABLE_NAME = "java-sdk-binary-" + System.currentTimeMillis(); - public static final String HASH_KEY_NAME = "hash"; - public static final Long READ_CAPACITY = 10L; - public static final Long WRITE_CAPACITY = 5L; - public static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = - ProvisionedThroughput.builder().readCapacityUnits(READ_CAPACITY).writeCapacityUnits(WRITE_CAPACITY).build(); - - @Override - protected DynamoDbClient getClient() { - return DynamoDBTestBase.getClient(); - } - - @Override - protected CreateTableRequest getCreateTableRequest() { - CreateTableRequest request = CreateTableRequest.builder() - .tableName(TEMP_BINARY_TABLE_NAME) - .keySchema( - KeySchemaElement.builder().attributeName(HASH_KEY_NAME) - .keyType(KeyType.HASH).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName( - HASH_KEY_NAME).attributeType( - ScalarAttributeType.B).build()) - .provisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT) - .build(); - return request; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TempTableWithSecondaryIndexes.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TempTableWithSecondaryIndexes.java deleted file mode 100644 index 07d827c164db..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TempTableWithSecondaryIndexes.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources.tables; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.Projection; -import software.amazon.awssdk.services.dynamodb.model.ProjectionType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import utils.test.resources.DynamoDBTableResource; -import utils.test.util.DynamoDBTestBase; - -/** - * The table used by SecondaryIndexesIntegrationTest - */ -public class TempTableWithSecondaryIndexes extends DynamoDBTableResource { - - public static final String TEMP_TABLE_NAME = "java-sdk-indexes-" + System.currentTimeMillis(); - public static final String HASH_KEY_NAME = "hash_key"; - public static final String RANGE_KEY_NAME = "range_key"; - public static final String LSI_NAME = "local_secondary_index"; - public static final String LSI_RANGE_KEY_NAME = "local_secondary_index_attribute"; - public static final String GSI_NAME = "global_secondary_index"; - public static final String GSI_HASH_KEY_NAME = "global_secondary_index_hash_attribute"; - public static final String GSI_RANGE_KEY_NAME = "global_secondary_index_range_attribute"; - public static final ProvisionedThroughput GSI_PROVISIONED_THROUGHPUT = ProvisionedThroughput.builder() - .readCapacityUnits(5L) - .writeCapacityUnits(5L) - .build(); - - @Override - protected DynamoDbClient getClient() { - return DynamoDBTestBase.getClient(); - } - - /** - * Table schema: - * Hash Key : HASH_KEY_NAME (S) - * Range Key : RANGE_KEY_NAME (N) - * LSI schema: - * Hash Key : HASH_KEY_NAME (S) - * Range Key : LSI_RANGE_KEY_NAME (N) - * GSI schema: - * Hash Key : GSI_HASH_KEY_NAME (N) - * Range Key : GSI_RANGE_KEY_NAME (N) - */ - @Override - protected CreateTableRequest getCreateTableRequest() { - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TEMP_TABLE_NAME) - .keySchema( - KeySchemaElement.builder() - .attributeName(HASH_KEY_NAME) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(RANGE_KEY_NAME) - .keyType(KeyType.RANGE) - .build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName( - HASH_KEY_NAME).attributeType( - ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName( - RANGE_KEY_NAME).attributeType( - ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName( - LSI_RANGE_KEY_NAME).attributeType( - ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName( - GSI_HASH_KEY_NAME).attributeType( - ScalarAttributeType.S).build(), - AttributeDefinition.builder().attributeName( - GSI_RANGE_KEY_NAME).attributeType( - ScalarAttributeType.N).build()) - .provisionedThroughput(BasicTempTable.DEFAULT_PROVISIONED_THROUGHPUT) - .localSecondaryIndexes( - LocalSecondaryIndex.builder() - .indexName(LSI_NAME) - .keySchema( - KeySchemaElement.builder() - .attributeName( - HASH_KEY_NAME) - .keyType(KeyType.HASH).build(), - KeySchemaElement.builder() - .attributeName( - LSI_RANGE_KEY_NAME) - .keyType(KeyType.RANGE).build()) - .projection( - Projection.builder() - .projectionType(ProjectionType.KEYS_ONLY).build()).build()) - .globalSecondaryIndexes( - GlobalSecondaryIndex.builder().indexName(GSI_NAME) - .keySchema( - KeySchemaElement.builder() - .attributeName( - GSI_HASH_KEY_NAME) - .keyType(KeyType.HASH).build(), - KeySchemaElement.builder() - .attributeName( - GSI_RANGE_KEY_NAME) - .keyType(KeyType.RANGE).build()) - .projection( - Projection.builder() - .projectionType(ProjectionType.KEYS_ONLY).build()) - .provisionedThroughput( - GSI_PROVISIONED_THROUGHPUT).build()) - .build(); - return createTableRequest; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TestTableForParallelScan.java b/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TestTableForParallelScan.java deleted file mode 100644 index e35c885e225f..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/resources/tables/TestTableForParallelScan.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.resources.tables; - -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; -import utils.test.resources.DynamoDBTableResource; -import utils.test.util.DynamoDBTestBase; - -/** - * Test table for {@link ParallelScanIntegrationTest} - */ -public class TestTableForParallelScan extends DynamoDBTableResource { - - public static final String TABLE_NAME = "java-sdk-parallel-scan"; - public static final String HASH_KEY_NAME = "hash"; - public static final Long READ_CAPACITY = 10L; - public static final Long WRITE_CAPACITY = 5L; - public static final ProvisionedThroughput DEFAULT_PROVISIONED_THROUGHPUT = - ProvisionedThroughput.builder().readCapacityUnits(READ_CAPACITY).writeCapacityUnits(WRITE_CAPACITY).build(); - - @Override - protected DynamoDbClient getClient() { - return DynamoDBTestBase.getClient(); - } - - @Override - protected CreateTableRequest getCreateTableRequest() { - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_NAME) - .keySchema( - KeySchemaElement.builder().attributeName(HASH_KEY_NAME) - .keyType(KeyType.HASH).build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName( - HASH_KEY_NAME).attributeType( - ScalarAttributeType.N).build()) - .provisionedThroughput(DEFAULT_PROVISIONED_THROUGHPUT) - .build(); - return createTableRequest; - } - -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/test/resources/DynamoDBTableResource.java b/test/dynamodbmapper-v1/src/test/java/utils/test/resources/DynamoDBTableResource.java deleted file mode 100644 index 9a822568601a..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/test/resources/DynamoDBTableResource.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.test.resources; - -import java.util.List; - -import software.amazon.awssdk.awscore.exception.AwsServiceException; -import software.amazon.awssdk.core.exception.SdkServiceException; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndexDescription; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndexDescription; -import software.amazon.awssdk.services.dynamodb.model.Projection; -import software.amazon.awssdk.services.dynamodb.model.TableDescription; -import software.amazon.awssdk.services.dynamodb.model.TableStatus; -import software.amazon.awssdk.testutils.UnorderedCollectionComparator; -import software.amazon.awssdk.utils.Logger; -import utils.resources.TestResource; -import utils.test.util.DynamoDBTestBase; - -public abstract class DynamoDBTableResource implements TestResource { - private static final Logger log = Logger.loggerFor(DynamoDBTableResource.class); - - /** - * Returns true if the two lists of GlobalSecondaryIndex and - * GlobalSecondaryIndexDescription share the same set of: - * 1) indexName - * 2) projection - * 3) keySchema (compared as unordered lists) - */ - static boolean equalUnorderedGsiLists(List listA, List listB) { - return UnorderedCollectionComparator.equalUnorderedCollections( - listA, listB, - new UnorderedCollectionComparator.CrossTypeComparator() { - @Override - public boolean equals(GlobalSecondaryIndex a, GlobalSecondaryIndexDescription b) { - return a.indexName().equals(b.indexName()) - && equalProjections(a.projection(), b.projection()) - && UnorderedCollectionComparator.equalUnorderedCollections(a.keySchema(), b.keySchema()); - } - }); - } - - /** - * Returns true if the two lists of LocalSecondaryIndex and - * LocalSecondaryIndexDescription share the same set of: - * 1) indexName - * 2) projection - * 3) keySchema (compared as unordered lists) - */ - static boolean equalUnorderedLsiLists(List listA, List listB) { - return UnorderedCollectionComparator.equalUnorderedCollections( - listA, listB, - new UnorderedCollectionComparator.CrossTypeComparator() { - @Override - public boolean equals(LocalSecondaryIndex a, LocalSecondaryIndexDescription b) { - // Project parameter might not be specified in the - // CreateTableRequest. But it should be treated as equal - // to the default projection type - KEYS_ONLY. - return a.indexName().equals(b.indexName()) - && equalProjections(a.projection(), b.projection()) - && UnorderedCollectionComparator.equalUnorderedCollections(a.keySchema(), b.keySchema()); - } - }); - } - - /** - * Compares the Projection parameter included in the CreateTableRequest, - * with the one returned from DescribeTableResponse. - */ - static boolean equalProjections(Projection fromCreateTableRequest, Projection fromDescribeTableResponse) { - if (fromCreateTableRequest == null || fromDescribeTableResponse == null) { - throw new IllegalStateException("The projection parameter should never be null."); - } - - return fromCreateTableRequest.projectionType().equals( - fromDescribeTableResponse.projectionType()) - && UnorderedCollectionComparator.equalUnorderedCollections( - fromCreateTableRequest.nonKeyAttributes(), - fromDescribeTableResponse.nonKeyAttributes()); - } - - protected abstract DynamoDbClient getClient(); - - protected abstract CreateTableRequest getCreateTableRequest(); - - /** - * Implementation of TestResource interfaces - */ - - @Override - public void create(boolean waitTillFinished) { - log.info(() -> "Creating " + this + "..."); - getClient().createTable(getCreateTableRequest()); - - if (waitTillFinished) { - log.info(() -> "Waiting for " + this + " to become active..."); - try { - TableUtils.waitUntilActive(getClient(), getCreateTableRequest().tableName()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - - @Override - public void delete(boolean waitTillFinished) { - log.info(() -> "Deleting " + this + "..."); - getClient().deleteTable(DeleteTableRequest.builder().tableName(getCreateTableRequest().tableName()).build()); - - if (waitTillFinished) { - log.info(() -> "Waiting for " + this + " to become deleted..."); - DynamoDBTestBase.waitForTableToBecomeDeleted(getClient(), getCreateTableRequest().tableName()); - } - } - - @Override - public ResourceStatus getResourceStatus() { - CreateTableRequest createRequest = getCreateTableRequest(); - TableDescription table = null; - try { - table = getClient().describeTable(DescribeTableRequest.builder().tableName( - createRequest.tableName()).build()).table(); - } catch (AwsServiceException exception) { - if (exception.awsErrorDetails().errorCode().equalsIgnoreCase("ResourceNotFoundException")) { - return ResourceStatus.NOT_EXIST; - } - } - - TableStatus tableStatus = table.tableStatus(); - - if (tableStatus == TableStatus.ACTIVE) { - // returns AVAILABLE only if table KeySchema + LSIs + GSIs all match. - if (UnorderedCollectionComparator.equalUnorderedCollections(createRequest.keySchema(), table.keySchema()) - && equalUnorderedGsiLists(createRequest.globalSecondaryIndexes(), table.globalSecondaryIndexes()) - && equalUnorderedLsiLists(createRequest.localSecondaryIndexes(), table.localSecondaryIndexes())) { - return ResourceStatus.AVAILABLE; - } else { - return ResourceStatus.EXIST_INCOMPATIBLE_RESOURCE; - } - } else if (tableStatus == TableStatus.CREATING - || tableStatus == TableStatus.UPDATING - || tableStatus == TableStatus.DELETING) { - return ResourceStatus.TRANSIENT; - } else { - return ResourceStatus.NOT_EXIST; - } - } - - /** - * Object interfaces - */ - @Override - public String toString() { - return "DynamoDB Table [" + getCreateTableRequest().tableName() + "]"; - } - - @Override - public int hashCode() { - return getCreateTableRequest().hashCode(); - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof DynamoDBTableResource)) { - return false; - } - return getCreateTableRequest().equals( - ((DynamoDBTableResource) other).getCreateTableRequest()); - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/test/util/DynamoDBIntegrationTestBase.java b/test/dynamodbmapper-v1/src/test/java/utils/test/util/DynamoDBIntegrationTestBase.java deleted file mode 100644 index 2ff2029118b2..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/test/util/DynamoDBIntegrationTestBase.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.test.util; - -import org.junit.BeforeClass; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.TableUtils; -import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition; -import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest; -import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest; -import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement; -import software.amazon.awssdk.services.dynamodb.model.KeyType; -import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest; -import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse; -import software.amazon.awssdk.services.dynamodb.model.LocalSecondaryIndex; -import software.amazon.awssdk.services.dynamodb.model.Projection; -import software.amazon.awssdk.services.dynamodb.model.ProjectionType; -import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput; -import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType; - -public class DynamoDBIntegrationTestBase extends DynamoDBTestBase { - - protected static final String KEY_NAME = "key"; - protected static final String TABLE_NAME = "aws-java-sdk-util"; - protected static final String TABLE_WITH_RANGE_ATTRIBUTE = "aws-java-sdk-range-test"; - protected static final String TABLE_WITH_INDEX_RANGE_ATTRIBUTE = "aws-java-sdk-index-range-test"; - protected static long startKey = System.currentTimeMillis(); - - @BeforeClass - public static void setUp() throws Exception { - setUpCredentials(); - dynamo = DynamoDbClient.builder().region(Region.US_EAST_1).credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - - // Create a table - String keyName = KEY_NAME; - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_NAME) - .keySchema(KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(keyName) - .attributeType(ScalarAttributeType.S) - .build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(10L) - .writeCapacityUnits(5L).build()) - .build(); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_NAME); - } - } - - /** - * Quick utility method to delete all tables when we have too much capacity - * reserved for the region. - */ - public static void deleteAllTables() { - ListTablesResponse listTables = dynamo.listTables(ListTablesRequest.builder().build()); - for (String name : listTables.tableNames()) { - dynamo.deleteTable(DeleteTableRequest.builder().tableName(name).build()); - } - } - - protected static void setUpTableWithRangeAttribute() throws Exception { - setUp(); - - String keyName = DynamoDBIntegrationTestBase.KEY_NAME; - String rangeKeyAttributeName = "rangeKey"; - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_WITH_RANGE_ATTRIBUTE) - .keySchema( - KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(rangeKeyAttributeName) - .keyType(KeyType.RANGE) - .build()) - .attributeDefinitions( - AttributeDefinition.builder() - .attributeName(keyName) - .attributeType(ScalarAttributeType.N) - .build(), - AttributeDefinition.builder() - .attributeName(rangeKeyAttributeName) - .attributeType(ScalarAttributeType.N) - .build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(10L) - .writeCapacityUnits(5L).build()) - .build(); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_WITH_RANGE_ATTRIBUTE); - } - } - - protected static void setUpTableWithIndexRangeAttribute(boolean recreateTable) throws Exception { - setUp(); - if (recreateTable) { - dynamo.deleteTable(DeleteTableRequest.builder().tableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE).build()); - waitForTableToBecomeDeleted(TABLE_WITH_INDEX_RANGE_ATTRIBUTE); - } - - String keyName = DynamoDBIntegrationTestBase.KEY_NAME; - String rangeKeyAttributeName = "rangeKey"; - String indexFooRangeKeyAttributeName = "indexFooRangeKey"; - String indexBarRangeKeyAttributeName = "indexBarRangeKey"; - String multipleIndexRangeKeyAttributeName = "multipleIndexRangeKey"; - String fooAttributeName = "fooAttribute"; - String barAttributeName = "barAttribute"; - String indexFooName = "index_foo"; - String indexBarName = "index_bar"; - String indexFooCopyName = "index_foo_copy"; - String indexBarCopyName = "index_bar_copy"; - - CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(TABLE_WITH_INDEX_RANGE_ATTRIBUTE) - .keySchema( - KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(rangeKeyAttributeName) - .keyType(KeyType.RANGE) - .build()) - .localSecondaryIndexes( - LocalSecondaryIndex.builder() - .indexName(indexFooName) - .keySchema( - KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(indexFooRangeKeyAttributeName) - .keyType(KeyType.RANGE) - .build()) - .projection(Projection.builder() - .projectionType(ProjectionType.INCLUDE) - .nonKeyAttributes(fooAttributeName) - .build()) - .build(), - LocalSecondaryIndex.builder() - .indexName(indexBarName) - .keySchema( - KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(indexBarRangeKeyAttributeName) - .keyType(KeyType.RANGE) - .build()) - .projection(Projection.builder() - .projectionType(ProjectionType.INCLUDE) - .nonKeyAttributes(barAttributeName) - .build()) - .build(), - LocalSecondaryIndex.builder() - .indexName(indexFooCopyName) - .keySchema( - KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(multipleIndexRangeKeyAttributeName) - .keyType(KeyType.RANGE) - .build()) - .projection(Projection.builder() - .projectionType(ProjectionType.INCLUDE) - .nonKeyAttributes(fooAttributeName) - .build()) - .build(), - LocalSecondaryIndex.builder() - .indexName(indexBarCopyName) - .keySchema( - KeySchemaElement.builder() - .attributeName(keyName) - .keyType(KeyType.HASH) - .build(), - KeySchemaElement.builder() - .attributeName(multipleIndexRangeKeyAttributeName) - .keyType(KeyType.RANGE) - .build()) - .projection(Projection.builder() - .projectionType(ProjectionType.INCLUDE) - .nonKeyAttributes(barAttributeName) - .build()) - .build()) - .attributeDefinitions( - AttributeDefinition.builder().attributeName(keyName).attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName(rangeKeyAttributeName) - .attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName(indexFooRangeKeyAttributeName) - .attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName(indexBarRangeKeyAttributeName) - .attributeType(ScalarAttributeType.N).build(), - AttributeDefinition.builder().attributeName(multipleIndexRangeKeyAttributeName) - .attributeType(ScalarAttributeType.N).build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(10L) - .writeCapacityUnits(5L).build()) - .build(); - - if (TableUtils.createTableIfNotExists(dynamo, createTableRequest)) { - TableUtils.waitUntilActive(dynamo, TABLE_WITH_INDEX_RANGE_ATTRIBUTE); - } - } -} diff --git a/test/dynamodbmapper-v1/src/test/java/utils/test/util/DynamoDBTestBase.java b/test/dynamodbmapper-v1/src/test/java/utils/test/util/DynamoDBTestBase.java deleted file mode 100644 index 5dd0d283a7bd..000000000000 --- a/test/dynamodbmapper-v1/src/test/java/utils/test/util/DynamoDBTestBase.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file 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 utils.test.util; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.math.BigDecimal; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import software.amazon.awssdk.core.exception.SdkClientException; -import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.dynamodb.DynamoDbClient; -import software.amazon.awssdk.services.dynamodb.model.AttributeValue; -import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException; -import software.amazon.awssdk.testutils.Waiter; -import software.amazon.awssdk.testutils.service.AwsTestBase; -import software.amazon.awssdk.utils.Logger; - -public class DynamoDBTestBase extends AwsTestBase { - protected static final String ENDPOINT = "http://dynamodb.us-east-1.amazonaws.com/"; - - protected static DynamoDbClient dynamo; - - private static final Logger log = Logger.loggerFor(DynamoDBTestBase.class); - - public static void setUpTestBase() { - try { - setUpCredentials(); - } catch (Exception e) { - throw SdkClientException.builder().message("Unable to load credential property file.").cause(e).build(); - } - - dynamo = DynamoDbClient.builder().region(Region.US_EAST_1).credentialsProvider(CREDENTIALS_PROVIDER_CHAIN).build(); - } - - public static DynamoDbClient getClient() { - if (dynamo == null) { - setUpTestBase(); - } - return dynamo; - } - - protected static void waitForTableToBecomeDeleted(String tableName) { - waitForTableToBecomeDeleted(dynamo, tableName); - } - - public static void waitForTableToBecomeDeleted(DynamoDbClient dynamo, String tableName) { - log.info(() -> "Waiting for " + tableName + " to become Deleted..."); - - Waiter.run(() -> dynamo.describeTable(r -> r.tableName(tableName))) - .untilException(ResourceNotFoundException.class) - .orFail(); - } - - protected static void assertSetsEqual(Collection expected, Collection given) { - Set givenCopy = new HashSet(given); - for (T e : expected) { - if (!givenCopy.remove(e)) { - fail("Expected element not found: " + e); - } - } - - assertTrue("Unexpected elements found: " + givenCopy, givenCopy.isEmpty()); - } - - /** - * Only valid for whole numbers - */ - protected static void assertNumericSetsEquals(Set expected, Collection given) { - Set givenCopy = new HashSet(); - for (String s : given) { - BigDecimal bd = new BigDecimal(s); - givenCopy.add(bd.setScale(0)); - } - - Set expectedCopy = new HashSet(); - for (Number n : expected) { - BigDecimal bd = new BigDecimal(n.toString()); - expectedCopy.add(bd.setScale(0)); - } - - assertSetsEqual(expectedCopy, givenCopy); - } - - protected static Set toSet(T... array) { - Set set = new HashSet(); - for (T t : array) { - set.add(t); - } - return set; - } - - protected static Set toSet(Collection collection) { - Set set = new HashSet(); - for (T t : collection) { - set.add(t); - } - return set; - } - - protected static byte[] generateByteArray(int length) { - byte[] bytes = new byte[length]; - for (int i = 0; i < length; i++) { - bytes[i] = (byte) (i % Byte.MAX_VALUE); - } - return bytes; - } - - /** - * Gets a map of key values for the single hash key attribute value given. - */ - protected Map mapKey(String attributeName, AttributeValue value) { - HashMap map = new HashMap(); - map.put(attributeName, value); - return map; - } - -} diff --git a/test/http-client-tests/pom.xml b/test/http-client-tests/pom.xml index 99521c27319c..c27a55cb6171 100644 --- a/test/http-client-tests/pom.xml +++ b/test/http-client-tests/pom.xml @@ -1,6 +1,6 @@ + ${awsjavasdk.version}-PREVIEW + + + com.amazonaws + aws-java-sdk-dynamodb + 1.11.748 + io.netty netty-tcnative-boringssl-static diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkResultProcessor.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkResultProcessor.java index 04452d3e8c4d..f74fa66261dc 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkResultProcessor.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkResultProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; +import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -76,7 +77,16 @@ List processBenchmarkResult(Collection results) { SdkBenchmarkResult sdkBenchmarkData = constructSdkBenchmarkResult(result); if (baselineResult == null) { - log.warn(() -> "Unable to find the baseline for " + benchmarkId + " Skipping regression validation"); + log.warn(() -> { + String benchmarkResultJson = null; + try { + benchmarkResultJson = OBJECT_MAPPER.writeValueAsString(sdkBenchmarkData); + } catch (IOException e) { + log.error(() -> "Unable to serialize result data to JSON"); + } + return String.format("Unable to find the baseline for %s. Skipping regression validation. " + + "Results were: %s", benchmarkId, benchmarkResultJson); + }); continue; } diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkRunner.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkRunner.java index d7be410f13a3..6f815db83983 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkRunner.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/BenchmarkRunner.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -36,6 +36,14 @@ import software.amazon.awssdk.benchmark.apicall.protocol.XmlProtocolBenchmark; import software.amazon.awssdk.benchmark.coldstart.V2DefaultClientCreationBenchmark; import software.amazon.awssdk.benchmark.coldstart.V2OptimizedClientCreationBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientGetOverheadBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientPutOverheadBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark; +import software.amazon.awssdk.benchmark.enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark; import software.amazon.awssdk.utils.Logger; @@ -58,6 +66,17 @@ public class BenchmarkRunner { V2OptimizedClientCreationBenchmark.class.getSimpleName(), V2DefaultClientCreationBenchmark.class.getSimpleName()); + private static final List MAPPER_BENCHMARKS = Arrays.asList( + EnhancedClientGetOverheadBenchmark.class.getSimpleName(), + EnhancedClientPutOverheadBenchmark.class.getSimpleName(), + EnhancedClientGetV1MapperComparisonBenchmark.class.getSimpleName(), + EnhancedClientPutV1MapperComparisonBenchmark.class.getSimpleName(), + EnhancedClientUpdateV1MapperComparisonBenchmark.class.getSimpleName(), + EnhancedClientDeleteV1MapperComparisonBenchmark.class.getSimpleName(), + EnhancedClientScanV1MapperComparisonBenchmark.class.getSimpleName(), + EnhancedClientQueryV1MapperComparisonBenchmark.class.getSimpleName() + ); + private static final Logger log = Logger.loggerFor(BenchmarkRunner.class); private final List benchmarksToRun; @@ -74,6 +93,7 @@ public static void main(String... args) throws RunnerException, JsonProcessingEx benchmarksToRun.addAll(ASYNC_BENCHMARKS); benchmarksToRun.addAll(PROTOCOL_BENCHMARKS); benchmarksToRun.addAll(COLD_START_BENCHMARKS); + benchmarksToRun.addAll(MAPPER_BENCHMARKS); BenchmarkRunner runner = new BenchmarkRunner(benchmarksToRun); diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/SdkHttpClientBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/SdkHttpClientBenchmark.java index 7aa8e4a5fc2f..c8448dfb37bc 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/SdkHttpClientBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/SdkHttpClientBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. @@ -38,5 +38,6 @@ public interface SdkHttpClientBenchmark { * * @param blackhole the blackhole */ - default void concurrentApiCall(Blackhole blackhole) {} + default void concurrentApiCall(Blackhole blackhole) { + } } diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/AwsCrtClientBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/AwsCrtClientBenchmark.java index 3bb6393a2060..377446824981 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/AwsCrtClientBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/AwsCrtClientBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/BaseNettyBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/BaseNettyBenchmark.java index 02aab48c03b9..fc84cffedac7 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/BaseNettyBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/BaseNettyBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyClientH1NonTlsBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyClientH1NonTlsBenchmark.java index 64331db4bb84..0ed7c72e2775 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyClientH1NonTlsBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyClientH1NonTlsBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH1Benchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH1Benchmark.java index a5f25615ec20..57217beba818 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH1Benchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH1Benchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH2Benchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH2Benchmark.java index cf4751eecdee..97c42bde324f 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH2Benchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/async/NettyHttpClientH2Benchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/ApacheHttpClientBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/ApacheHttpClientBenchmark.java index 3f6597b682b0..9dc7319184a9 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/ApacheHttpClientBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/ApacheHttpClientBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/UrlConnectionHttpClientBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/UrlConnectionHttpClientBenchmark.java index bc9f5130eaae..3ff99b5dcd0b 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/UrlConnectionHttpClientBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/httpclient/sync/UrlConnectionHttpClientBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/Ec2ProtocolBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/Ec2ProtocolBenchmark.java index 99d3a97c59a3..e62142e97600 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/Ec2ProtocolBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/Ec2ProtocolBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/JsonProtocolBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/JsonProtocolBenchmark.java index 30a0095c52e7..0229c628b92a 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/JsonProtocolBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/JsonProtocolBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/QueryProtocolBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/QueryProtocolBenchmark.java index 744c1df6566a..10844a61fd95 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/QueryProtocolBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/QueryProtocolBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/SdkProtocolBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/SdkProtocolBenchmark.java index 84aa5e178ffc..30b2d7c0d421 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/SdkProtocolBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/SdkProtocolBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/XmlProtocolBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/XmlProtocolBenchmark.java index 7d87e434ff3a..e141bb8a5385 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/XmlProtocolBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/apicall/protocol/XmlProtocolBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/SdkClientCreationBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/SdkClientCreationBenchmark.java index 1fd711c87b9e..84a9a2cb8e62 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/SdkClientCreationBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/SdkClientCreationBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V1ClientCreationBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V1ClientCreationBenchmark.java index fedfe59e46c8..46bfa00ca8a8 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V1ClientCreationBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V1ClientCreationBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2DefaultClientCreationBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2DefaultClientCreationBenchmark.java index dd7e8c6bc988..f966642a23ce 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2DefaultClientCreationBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2DefaultClientCreationBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2OptimizedClientCreationBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2OptimizedClientCreationBenchmark.java index 816e2adaf2df..b22a7c39119e 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2OptimizedClientCreationBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/coldstart/V2OptimizedClientCreationBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientDeleteV1MapperComparisonBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientDeleteV1MapperComparisonBenchmark.java new file mode 100644 index 000000000000..063b78bffd0a --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientDeleteV1MapperComparisonBenchmark.java @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientDeleteV1MapperComparisonBenchmark { + @Benchmark + public void v2Delete(TestState s) { + s.v2Table.deleteItem(s.key); + } + + @Benchmark + public void v1Delete(TestState s) { + s.v1DdbMapper.delete(s.testItem.v1Key); + } + + private static DynamoDbClient getV2Client(Blackhole bh) { + return new V2TestDynamoDbDeleteItemClient(bh); + } + + private static AmazonDynamoDB getV1Client(Blackhole bh) { + return new V1TestDynamoDbDeleteItemClient(bh); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + public TestItem testItem; + + private final Key key = Key.builder().partitionValue("key").build(); + + private DynamoDbTable v2Table; + private DynamoDBMapper v1DdbMapper; + + + @Setup + public void setup(Blackhole bh) { + DynamoDbEnhancedClient v2DdbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getV2Client(bh)) + .build(); + + v2Table = v2DdbEnh.table(testItem.name(), testItem.schema); + + v1DdbMapper = new DynamoDBMapper(getV1Client(bh)); + } + + public enum TestItem { + TINY( + V2ItemFactory.TINY_BEAN_TABLE_SCHEMA, + new V1ItemFactory.V1TinyBean("hashKey") + ), + + SMALL( + V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA, + new V1ItemFactory.V1SmallBean("hashKey") + ), + + HUGE( + V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA, + new V1ItemFactory.V1HugeBean("hashKey") + + ), + + HUGE_FLAT( + V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA, + new V1ItemFactory.V1HugeBeanFlat("hashKey") + ), + ; + + // V2 + private TableSchema schema; + + // V1 + private Object v1Key; + + TestItem(TableSchema schema, + Object v1Key) { + this.schema = schema; + + this.v1Key = v1Key; + } + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientGetOverheadBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientGetOverheadBenchmark.java new file mode 100644 index 000000000000..ce2fdf38786a --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientGetOverheadBenchmark.java @@ -0,0 +1,149 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import static software.amazon.awssdk.core.client.config.SdkClientOption.ENDPOINT; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.util.Map; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.benchmark.utils.MockHttpClient; +import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.protocols.json.AwsJsonProtocol; +import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.DynamoDbException; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.transform.PutItemRequestMarshaller; +import software.amazon.awssdk.utils.IoUtils; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientGetOverheadBenchmark { + private static final AwsJsonProtocolFactory JSON_PROTOCOL_FACTORY = AwsJsonProtocolFactory + .builder() + .clientConfiguration(SdkClientConfiguration.builder() + .option(ENDPOINT, URI.create("https://dynamodb.amazonaws.com")) + .build()) + .defaultServiceExceptionSupplier(DynamoDbException::builder) + .protocol(AwsJsonProtocol.AWS_JSON) + .protocolVersion("1.0") + .build(); + + private static final PutItemRequestMarshaller PUT_ITEM_REQUEST_MARSHALLER = + new PutItemRequestMarshaller(JSON_PROTOCOL_FACTORY); + + private static final V2ItemFactory ITEM_FACTORY = new V2ItemFactory(); + + private final Key testKey = Key.builder().partitionValue("key").build(); + + + @Benchmark + public Object lowLevelGet(TestState s) { + return s.dynamoDb.getItem(GetItemRequest.builder().build()); + } + + @Benchmark + public Object enhanceGet(TestState s) { + return s.table.getItem(testKey); + } + + @State(Scope.Benchmark) + public static class TestState { + private DynamoDbClient dynamoDb; + + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + private TestItem testItem; + + private DynamoDbTable table; + + @Setup + public void setup(Blackhole bh) { + dynamoDb = DynamoDbClient.builder() + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))) + .httpClient(new MockHttpClient(testItem.responseContent, "{}")) + .overrideConfiguration(o -> o.addExecutionInterceptor(new ExecutionInterceptor() { + @Override + public void afterUnmarshalling(Context.AfterUnmarshalling context, + ExecutionAttributes executionAttributes) { + bh.consume(context); + bh.consume(executionAttributes); + } + })) + .build(); + + DynamoDbEnhancedClient ddbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(dynamoDb) + .build(); + + table = ddbEnh.table(testItem.name(), testItem.tableSchema); + } + } + + public enum TestItem { + TINY(marshall(ITEM_FACTORY.tiny()), V2ItemFactory.TINY_BEAN_TABLE_SCHEMA), + SMALL(marshall(ITEM_FACTORY.small()), V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA), + HUGE(marshall(ITEM_FACTORY.huge()), V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA), + HUGE_FLAT(marshall(ITEM_FACTORY.hugeFlat()), V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA) + ; + + private String responseContent; + private TableSchema tableSchema; + + TestItem(String responseContent, TableSchema tableSchema) { + this.responseContent = responseContent; + this.tableSchema = tableSchema; + } + } + + private static String marshall(Map item) { + return PUT_ITEM_REQUEST_MARSHALLER.marshall(PutItemRequest.builder().item(item).build()) + .contentStreamProvider().map(cs -> { + try { + return IoUtils.toUtf8String(cs.newStream()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }).orElse(null); + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientGetV1MapperComparisonBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientGetV1MapperComparisonBenchmark.java new file mode 100644 index 000000000000..12bc62d18454 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientGetV1MapperComparisonBenchmark.java @@ -0,0 +1,143 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.GetItemResult; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientGetV1MapperComparisonBenchmark { + private static final V2ItemFactory V2_ITEM_FACTORY = new V2ItemFactory(); + private static final V1ItemFactory V1_ITEM_FACTORY = new V1ItemFactory(); + + @Benchmark + public Object v2Get(TestState s) { + return s.v2Table.getItem(s.key); + } + + @Benchmark + public Object v1Get(TestState s) { + return s.v1DdbMapper.load(s.testItem.v1Key); + } + + private static DynamoDbClient getV2Client(Blackhole bh, GetItemResponse getItemResponse) { + return new V2TestDynamoDbGetItemClient(bh, getItemResponse); + } + + private static AmazonDynamoDB getV1Client(Blackhole bh, GetItemResult getItemResult) { + return new V1TestDynamoDbGetItemClient(bh, getItemResult); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + public TestItem testItem; + + private final Key key = Key.builder().partitionValue("key").build(); + + private DynamoDbTable v2Table; + private DynamoDBMapper v1DdbMapper; + + + @Setup + public void setup(Blackhole bh) { + DynamoDbEnhancedClient v2DdbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getV2Client(bh, testItem.v2Response)) + .build(); + + v2Table = v2DdbEnh.table(testItem.name(), testItem.schema); + + v1DdbMapper = new DynamoDBMapper(getV1Client(bh, testItem.v1Response)); + } + + public enum TestItem { + TINY( + V2ItemFactory.TINY_BEAN_TABLE_SCHEMA, + GetItemResponse.builder().item(V2_ITEM_FACTORY.tiny()).build(), + + new V1ItemFactory.V1TinyBean("hashKey"), + new GetItemResult().withItem(V1_ITEM_FACTORY.tiny()) + ), + + SMALL( + V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA, + GetItemResponse.builder().item(V2_ITEM_FACTORY.small()).build(), + + new V1ItemFactory.V1SmallBean("hashKey"), + new GetItemResult().withItem(V1_ITEM_FACTORY.small()) + ), + + HUGE( + V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA, + GetItemResponse.builder().item(V2_ITEM_FACTORY.huge()).build(), + + new V1ItemFactory.V1HugeBean("hashKey"), + new GetItemResult().withItem(V1_ITEM_FACTORY.huge()) + ), + + HUGE_FLAT( + V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA, + GetItemResponse.builder().item(V2_ITEM_FACTORY.hugeFlat()).build(), + + new V1ItemFactory.V1HugeBeanFlat("hashKey"), + new GetItemResult().withItem(V1_ITEM_FACTORY.hugeFlat()) + ), + ; + + // V2 + private TableSchema schema; + private GetItemResponse v2Response; + + // V1 + private Object v1Key; + private GetItemResult v1Response; + + TestItem(TableSchema schema, + GetItemResponse v2Response, + + Object v1Key, + GetItemResult v1Response) { + this.schema = schema; + this.v2Response = v2Response; + + this.v1Key = v1Key; + this.v1Response = v1Response; + } + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientPutOverheadBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientPutOverheadBenchmark.java new file mode 100644 index 000000000000..a6e0f93c2924 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientPutOverheadBenchmark.java @@ -0,0 +1,122 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import java.util.Map; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.benchmark.utils.MockHttpClient; +import software.amazon.awssdk.core.interceptor.Context; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientPutOverheadBenchmark { + @Benchmark + public void lowLevelPut(TestState s) { + s.ddb.putItem(r -> r.item(s.testItem.av)); + } + + @Benchmark + public void enhancedPut(TestState s) { + s.enhTable.putItem(s.testItem.bean); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + private TestItem testItem; + private DynamoDbClient ddb; + + private DynamoDbTable enhTable; + + @Setup + public void setup(Blackhole bh) { + ddb = DynamoDbClient.builder() + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("akid", "skid"))) + .httpClient(new MockHttpClient("{}", "{}")) + .overrideConfiguration(c -> c.addExecutionInterceptor(new ExecutionInterceptor() { + @Override + public void afterUnmarshalling(Context.AfterUnmarshalling context, + ExecutionAttributes executionAttributes) { + bh.consume(context); + bh.consume(executionAttributes); + } + })) + .build(); + + DynamoDbEnhancedClient ddbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(ddb) + .build(); + + enhTable = ddbEnh.table(testItem.name(), testItem.tableSchema); + } + } + + public enum TestItem { + TINY, + SMALL, + HUGE, + HUGE_FLAT + ; + + private static final V2ItemFactory FACTORY = new V2ItemFactory(); + + private Map av; + + private TableSchema tableSchema; + private Object bean; + + static { + TINY.av = FACTORY.tiny(); + TINY.tableSchema = V2ItemFactory.TINY_BEAN_TABLE_SCHEMA; + TINY.bean = FACTORY.tinyBean(); + + SMALL.av = FACTORY.small(); + SMALL.tableSchema = V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA; + SMALL.bean = FACTORY.smallBean(); + + HUGE.av = FACTORY.huge(); + HUGE.tableSchema = V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA; + HUGE.bean = FACTORY.hugeBean(); + + HUGE_FLAT.av = FACTORY.hugeFlat(); + HUGE_FLAT.tableSchema = V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA; + HUGE_FLAT.bean = FACTORY.hugeBeanFlat(); + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientPutV1MapperComparisonBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientPutV1MapperComparisonBenchmark.java new file mode 100644 index 000000000000..cd233506b282 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientPutV1MapperComparisonBenchmark.java @@ -0,0 +1,136 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientPutV1MapperComparisonBenchmark { + private static final V2ItemFactory V2_ITEM_FACTORY = new V2ItemFactory(); + private static final V1ItemFactory V1_ITEM_FACTORY = new V1ItemFactory(); + private static final DynamoDBMapperConfig MAPPER_CONFIG = + DynamoDBMapperConfig.builder() + .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.PUT) + .build(); + + @Benchmark + public void v2Put(TestState s) { + s.v2Table.putItem(s.testItem.v2Bean); + } + + @Benchmark + public void v1Put(TestState s) { + s.v1DdbMapper.save(s.testItem.v1Bean); + } + + private static DynamoDbClient getV2Client(Blackhole bh) { + return new V2TestDynamoDbPutItemClient(bh); + } + + private static AmazonDynamoDB getV1Client(Blackhole bh) { + return new V1TestDynamoDbPutItemClient(bh); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + public TestItem testItem; + + private DynamoDbTable v2Table; + private DynamoDBMapper v1DdbMapper; + + + @Setup + public void setup(Blackhole bh) { + DynamoDbEnhancedClient v2DdbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getV2Client(bh)) + .build(); + + v2Table = v2DdbEnh.table(testItem.name(), testItem.schema); + + v1DdbMapper = new DynamoDBMapper(getV1Client(bh), MAPPER_CONFIG); + } + + public enum TestItem { + TINY( + V2ItemFactory.TINY_BEAN_TABLE_SCHEMA, + V2_ITEM_FACTORY.tinyBean(), + + V1_ITEM_FACTORY.v1TinyBean() + ), + + SMALL( + V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA, + V2_ITEM_FACTORY.smallBean(), + + V1_ITEM_FACTORY.v1SmallBean() + ), + + HUGE( + V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA, + V2_ITEM_FACTORY.hugeBean(), + + V1_ITEM_FACTORY.v1hugeBean() + ), + + HUGE_FLAT( + V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA, + V2_ITEM_FACTORY.hugeBeanFlat(), + + V1_ITEM_FACTORY.v1HugeBeanFlat() + ), + ; + + // V2 + private TableSchema schema; + private Object v2Bean; + + // V1 + private Object v1Bean; + + TestItem(TableSchema schema, + Object v2Bean, + + Object v1Bean) { + this.schema = schema; + this.v2Bean = v2Bean; + + this.v1Bean = v1Bean; + } + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientQueryV1MapperComparisonBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientQueryV1MapperComparisonBenchmark.java new file mode 100644 index 000000000000..b25b560dbdc6 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientQueryV1MapperComparisonBenchmark.java @@ -0,0 +1,175 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression; +import com.amazonaws.services.dynamodbv2.model.QueryResult; +import java.util.Arrays; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.Key; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.enhanced.dynamodb.model.QueryConditional; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.QueryResponse; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientQueryV1MapperComparisonBenchmark { + private static final V2ItemFactory V2_ITEM_FACTORY = new V2ItemFactory(); + private static final V1ItemFactory V1_ITEM_FACTORY = new V1ItemFactory(); + + @Benchmark + public Object v2Query(TestState s) { + return s.v2Table.query(QueryConditional.keyEqualTo(s.key)).iterator().next(); + } + + @Benchmark + public Object v1Query(TestState s) { + return s.v1DdbMapper.query(s.testItem.getV1BeanClass(), s.testItem.v1QueryExpression).iterator().next(); + } + + private static DynamoDbClient getV2Client(Blackhole bh, QueryResponse queryResponse) { + return new V2TestDynamoDbQueryClient(bh, queryResponse); + } + + private static AmazonDynamoDB getV1Client(Blackhole bh, QueryResult queryResult) { + return new V1TestDynamoDbQueryClient(bh, queryResult); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + public TestItem testItem; + + private DynamoDbTable v2Table; + private DynamoDBMapper v1DdbMapper; + + private final Key key = Key.builder().partitionValue("key").build(); + + @Setup + public void setup(Blackhole bh) { + DynamoDbEnhancedClient v2DdbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getV2Client(bh, testItem.v2Response)) + .build(); + + v2Table = v2DdbEnh.table(testItem.name(), testItem.schema); + + v1DdbMapper = new DynamoDBMapper(getV1Client(bh, testItem.v1Response)); + } + + public enum TestItem { + TINY( + V2ItemFactory.TINY_BEAN_TABLE_SCHEMA, + QueryResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.tiny(), + V2_ITEM_FACTORY.tiny(), + V2_ITEM_FACTORY.tiny())) + .build(), + + V1ItemFactory.V1TinyBean.class, + new DynamoDBQueryExpression().withHashKeyValues(new V1ItemFactory.V1TinyBean("hashKey")), + new QueryResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.tiny(), V1_ITEM_FACTORY.tiny(), V1_ITEM_FACTORY.tiny())) + ), + SMALL( + V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA, + QueryResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.small(), + V2_ITEM_FACTORY.small(), + V2_ITEM_FACTORY.small())) + .build(), + + V1ItemFactory.V1SmallBean.class, + new DynamoDBQueryExpression().withHashKeyValues(new V1ItemFactory.V1SmallBean("hashKey")), + new QueryResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.small(), V1_ITEM_FACTORY.small(), V1_ITEM_FACTORY.small())) + ), + + HUGE( + V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA, + QueryResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.huge(), + V2_ITEM_FACTORY.huge(), + V2_ITEM_FACTORY.huge())) + .build(), + + V1ItemFactory.V1HugeBean.class, + new DynamoDBQueryExpression().withHashKeyValues(new V1ItemFactory.V1HugeBean("hashKey")), + new QueryResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.huge(), V1_ITEM_FACTORY.huge(), V1_ITEM_FACTORY.huge())) + ), + + HUGE_FLAT( + V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA, + QueryResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.hugeFlat(), + V2_ITEM_FACTORY.hugeFlat(), + V2_ITEM_FACTORY.hugeFlat())) + .build(), + + V1ItemFactory.V1HugeBeanFlat.class, + new DynamoDBQueryExpression().withHashKeyValues(new V1ItemFactory.V1HugeBeanFlat("hashKey")), + new QueryResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.hugeFlat(), V1_ITEM_FACTORY.hugeFlat(), V1_ITEM_FACTORY.hugeFlat())) + ), + ; + + // V2 + private TableSchema schema; + private QueryResponse v2Response; + + // V1 + private Class v1BeanClass; + private DynamoDBQueryExpression v1QueryExpression; + private QueryResult v1Response; + + TestItem(TableSchema schema, + QueryResponse v2Response, + + Class v1BeanClass, + DynamoDBQueryExpression v1QueryExpression, + QueryResult v1Response) { + this.schema = schema; + this.v2Response = v2Response; + + this.v1BeanClass = v1BeanClass; + this.v1QueryExpression = v1QueryExpression; + this.v1Response = v1Response; + } + + public Class getV1BeanClass() { + return v1BeanClass; + } + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientScanV1MapperComparisonBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientScanV1MapperComparisonBenchmark.java new file mode 100644 index 000000000000..f1875255adc2 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientScanV1MapperComparisonBenchmark.java @@ -0,0 +1,166 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; +import com.amazonaws.services.dynamodbv2.model.ScanResult; +import java.util.Arrays; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.ScanResponse; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientScanV1MapperComparisonBenchmark { + private static final V2ItemFactory V2_ITEM_FACTORY = new V2ItemFactory(); + private static final V1ItemFactory V1_ITEM_FACTORY = new V1ItemFactory(); + private static final DynamoDBScanExpression V1_SCAN_EXPRESSION = new DynamoDBScanExpression(); + + @Benchmark + public Object v2Scan(TestState s) { + return s.v2Table.scan().iterator().next(); + } + + @Benchmark + public Object v1Scan(TestState s) { + return s.v1DdbMapper.scan(s.testItem.getV1BeanClass(), V1_SCAN_EXPRESSION).iterator().next(); + } + + private static DynamoDbClient getV2Client(Blackhole bh, ScanResponse scanResponse) { + return new V2TestDynamoDbScanClient(bh, scanResponse); + } + + private static AmazonDynamoDB getV1Client(Blackhole bh, ScanResult scanResult) { + return new V1TestDynamoDbScanClient(bh, scanResult); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + public TestItem testItem; + + private DynamoDbTable v2Table; + private DynamoDBMapper v1DdbMapper; + + + @Setup + public void setup(Blackhole bh) { + DynamoDbEnhancedClient v2DdbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getV2Client(bh, testItem.v2Response)) + .build(); + + v2Table = v2DdbEnh.table(testItem.name(), testItem.schema); + + v1DdbMapper = new DynamoDBMapper(getV1Client(bh, testItem.v1Response)); + } + + public enum TestItem { + TINY( + V2ItemFactory.TINY_BEAN_TABLE_SCHEMA, + ScanResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.tiny(), + V2_ITEM_FACTORY.tiny(), + V2_ITEM_FACTORY.tiny())) + .build(), + + V1ItemFactory.V1TinyBean.class, + new ScanResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.tiny(), V1_ITEM_FACTORY.tiny(), V1_ITEM_FACTORY.tiny())) + ), + SMALL( + V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA, + ScanResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.small(), + V2_ITEM_FACTORY.small(), + V2_ITEM_FACTORY.small())) + .build(), + + V1ItemFactory.V1SmallBean.class, + new ScanResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.small(), V1_ITEM_FACTORY.small(), V1_ITEM_FACTORY.small())) + ), + + HUGE( + V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA, + ScanResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.huge(), + V2_ITEM_FACTORY.huge(), + V2_ITEM_FACTORY.huge())) + .build(), + + V1ItemFactory.V1HugeBean.class, + new ScanResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.huge(), V1_ITEM_FACTORY.huge(), V1_ITEM_FACTORY.huge())) + ), + + HUGE_FLAT( + V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA, + ScanResponse.builder() + .items(Arrays.asList(V2_ITEM_FACTORY.hugeFlat(), + V2_ITEM_FACTORY.hugeFlat(), + V2_ITEM_FACTORY.hugeFlat())) + .build(), + + V1ItemFactory.V1HugeBeanFlat.class, + new ScanResult().withItems( + Arrays.asList(V1_ITEM_FACTORY.hugeFlat(), V1_ITEM_FACTORY.hugeFlat(), V1_ITEM_FACTORY.hugeFlat())) + ), + ; + + // V2 + private TableSchema schema; + private ScanResponse v2Response; + + // V1 + private Class v1BeanClass; + private ScanResult v1Response; + + TestItem(TableSchema schema, + ScanResponse v2Response, + + Class v1BeanClass, + ScanResult v1Response) { + this.schema = schema; + this.v2Response = v2Response; + + this.v1BeanClass = v1BeanClass; + this.v1Response = v1Response; + } + + public Class getV1BeanClass() { + return v1BeanClass; + } + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientUpdateV1MapperComparisonBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientUpdateV1MapperComparisonBenchmark.java new file mode 100644 index 000000000000..a538526b6470 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/EnhancedClientUpdateV1MapperComparisonBenchmark.java @@ -0,0 +1,152 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig; +import com.amazonaws.services.dynamodbv2.model.UpdateItemResult; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient; +import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse; + +@BenchmarkMode(Mode.Throughput) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +@State(Scope.Benchmark) +public class EnhancedClientUpdateV1MapperComparisonBenchmark { + private static final V2ItemFactory V2_ITEM_FACTORY = new V2ItemFactory(); + private static final V1ItemFactory V1_ITEM_FACTORY = new V1ItemFactory(); + private static final DynamoDBMapperConfig MAPPER_CONFIG = + DynamoDBMapperConfig.builder() + .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.UPDATE) + .build(); + + @Benchmark + public void v2Update(TestState s) { + s.v2Table.updateItem(s.testItem.v2Bean); + } + + @Benchmark + public void v1Update(TestState s) { + s.v1DdbMapper.save(s.testItem.v1Bean); + } + + private static DynamoDbClient getV2Client(Blackhole bh, UpdateItemResponse updateItemResponse) { + return new V2TestDynamoDbUpdateItemClient(bh, updateItemResponse); + } + + private static AmazonDynamoDB getV1Client(Blackhole bh, UpdateItemResult updateItemResult) { + return new V1TestDynamoDbUpdateItemClient(bh, updateItemResult); + } + + @State(Scope.Benchmark) + public static class TestState { + @Param({"TINY", "SMALL", "HUGE", "HUGE_FLAT"}) + public TestItem testItem; + + private DynamoDbTable v2Table; + private DynamoDBMapper v1DdbMapper; + + + @Setup + public void setup(Blackhole bh) { + DynamoDbEnhancedClient v2DdbEnh = DynamoDbEnhancedClient.builder() + .dynamoDbClient(getV2Client(bh, testItem.v2UpdateItemResponse)) + .build(); + + v2Table = v2DdbEnh.table(testItem.name(), testItem.schema); + + v1DdbMapper = new DynamoDBMapper(getV1Client(bh, testItem.v1UpdateItemResult), MAPPER_CONFIG); + } + + public enum TestItem { + TINY( + V2ItemFactory.TINY_BEAN_TABLE_SCHEMA, + V2_ITEM_FACTORY.tinyBean(), + UpdateItemResponse.builder().attributes(V2_ITEM_FACTORY.tiny()).build(), + + V1_ITEM_FACTORY.v1TinyBean(), + new UpdateItemResult().withAttributes(V1_ITEM_FACTORY.tiny()) + ), + + SMALL( + V2ItemFactory.SMALL_BEAN_TABLE_SCHEMA, + V2_ITEM_FACTORY.smallBean(), + UpdateItemResponse.builder().attributes(V2_ITEM_FACTORY.small()).build(), + + V1_ITEM_FACTORY.v1SmallBean(), + new UpdateItemResult().withAttributes(V1_ITEM_FACTORY.small()) + ), + + HUGE( + V2ItemFactory.HUGE_BEAN_TABLE_SCHEMA, + V2_ITEM_FACTORY.hugeBean(), + UpdateItemResponse.builder().attributes(V2_ITEM_FACTORY.huge()).build(), + + V1_ITEM_FACTORY.v1hugeBean(), + new UpdateItemResult().withAttributes(V1_ITEM_FACTORY.huge()) + ), + + HUGE_FLAT( + V2ItemFactory.HUGE_BEAN_FLAT_TABLE_SCHEMA, + V2_ITEM_FACTORY.hugeBeanFlat(), + UpdateItemResponse.builder().attributes(V2_ITEM_FACTORY.hugeFlat()).build(), + + V1_ITEM_FACTORY.v1HugeBeanFlat(), + new UpdateItemResult().withAttributes(V1_ITEM_FACTORY.hugeFlat()) + ), + ; + + // V2 + private TableSchema schema; + private Object v2Bean; + private UpdateItemResponse v2UpdateItemResponse; + + // V1 + private Object v1Bean; + private UpdateItemResult v1UpdateItemResult; + + TestItem(TableSchema schema, + Object v2Bean, + UpdateItemResponse v2UpdateItemResponse, + + Object v1Bean, + UpdateItemResult v1UpdateItemResult) { + this.schema = schema; + this.v2Bean = v2Bean; + this.v2UpdateItemResponse = v2UpdateItemResponse; + + this.v1Bean = v1Bean; + this.v1UpdateItemResult = v1UpdateItemResult; + } + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/ItemFactory.java new file mode 100755 index 000000000000..4ac7ecc8d6bd --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/ItemFactory.java @@ -0,0 +1,860 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.util.ImmutableMapParameter; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import software.amazon.awssdk.core.SdkBytes; + +abstract class ItemFactory { + private static final String ALPHA = "abcdefghijklmnopqrstuvwxyz"; + + private static final Random RNG = new Random(); + + public final Map tiny() { + return asItem(tinyBean()); + } + + public final Map small() { + return asItem(smallBean()); + } + + public final Map huge() { + return asItem(hugeBean()); + } + + public final Map hugeFlat() { + return asItem(hugeBeanFlat()); + } + + public final TinyBean tinyBean() { + TinyBean b = new TinyBean(); + b.setStringAttr(randomS()); + return b; + } + + public final SmallBean smallBean() { + SmallBean b = new SmallBean(); + b.setStringAttr(randomS()); + b.setBinaryAttr(randomBytes()); + b.setListAttr(Arrays.asList(randomS(), randomS(), randomS())); + return b; + } + + public final HugeBean hugeBean() { + HugeBean b = new HugeBean(); + b.setHashKey(randomS()); + b.setStringAttr(randomS()); + b.setBinaryAttr(randomBytes()); + b.setListAttr(IntStream.range(0, 32).mapToObj(i -> randomS()).collect(Collectors.toList())); + + Map mapAttr1 = new HashMap<>(); + mapAttr1.put("key1", randomBytes()); + mapAttr1.put("key2", randomBytes()); + mapAttr1.put("key3", randomBytes()); + + b.setMapAttr1(mapAttr1); + + Map> mapAttr2 = new HashMap<>(); + mapAttr2.put("key1", Arrays.asList(randomBytes())); + mapAttr2.put("key2", IntStream.range(0, 2).mapToObj(i -> randomBytes()).collect(Collectors.toList())); + mapAttr2.put("key3", IntStream.range(0, 4).mapToObj(i -> randomBytes()).collect(Collectors.toList())); + mapAttr2.put("key4", IntStream.range(0, 8).mapToObj(i -> randomBytes()).collect(Collectors.toList())); + mapAttr2.put("key5", IntStream.range(0, 16).mapToObj(i -> randomBytes()).collect(Collectors.toList())); + + b.setMapAttr2(mapAttr2); + + ImmutableMapParameter.Builder>>> mapAttr3Builder = + ImmutableMapParameter.builder(); + + List>> value = Arrays.asList( + ImmutableMapParameter.>builder() + .put("key1", IntStream.range(0, 2).mapToObj(i -> randomBytes()).collect(Collectors.toList())) + .build(), + ImmutableMapParameter.>builder() + .put("key2", IntStream.range(0, 4).mapToObj(i -> randomBytes()).collect(Collectors.toList())) + .build(), + ImmutableMapParameter.>builder() + .put("key3", IntStream.range(0, 8).mapToObj(i -> randomBytes()).collect(Collectors.toList())) + .build() + ); + + mapAttr3Builder.put("key1", value) + .put("key2", value) + .build(); + + b.setMapAttr3(mapAttr3Builder.build()); + + return b; + } + + public HugeBeanFlat hugeBeanFlat() { + HugeBeanFlat b = new HugeBeanFlat(); + Class clazz = HugeBeanFlat.class; + for (int i = 1; i <= 63; ++i) { + try { + Method setter = clazz.getMethod("setStringAttr" + i, String.class); + setter.setAccessible(true); + setter.invoke(b, randomS()); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + return b; + } + + protected abstract Map asItem(TinyBean b); + + protected abstract Map asItem(SmallBean b); + + protected abstract Map asItem(HugeBean b); + + protected final Map asItem(HugeBeanFlat b) { + Map item = new HashMap<>(); + Class clazz = HugeBeanFlat.class; + for (int i = 1; i <= 63; ++i) { + try { + Method getter = clazz.getMethod("getStringAttr" + i); + getter.setAccessible(true); + item.put("stringAttr" + i, av((String) getter.invoke(b))); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + return item; + } + + protected abstract T av(String val); + + protected abstract T av(List val); + + protected abstract T av(Map val); + + protected abstract T av(SdkBytes val); + + private static String randomS(int len) { + StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; ++i) { + sb.append(ALPHA.charAt(RNG.nextInt(ALPHA.length()))); + } + return sb.toString(); + } + + private static String randomS() { + return randomS(16); + } + + private static ByteBuffer randomB(int len) { + byte[] b = new byte[len]; + RNG.nextBytes(b); + return ByteBuffer.wrap(b); + } + + private static ByteBuffer randomB() { + return randomB(16); + } + + private static SdkBytes randomBytes() { + return SdkBytes.fromByteBuffer(randomB()); + } + + public static class TinyBean { + private String stringAttr; + + public String getStringAttr() { + return stringAttr; + } + + public void setStringAttr(String stringAttr) { + this.stringAttr = stringAttr; + } + } + + public static class SmallBean { + private String stringAttr; + private SdkBytes binaryAttr; + private List listAttr; + + public String getStringAttr() { + return stringAttr; + } + + public void setStringAttr(String stringAttr) { + this.stringAttr = stringAttr; + } + + public SdkBytes getBinaryAttr() { + return binaryAttr; + } + + public void setBinaryAttr(SdkBytes binaryAttr) { + this.binaryAttr = binaryAttr; + } + + public List getListAttr() { + return listAttr; + } + + public void setListAttr(List listAttr) { + this.listAttr = listAttr; + } + } + + public static class HugeBean { + private String hashKey; + private String stringAttr; + private SdkBytes binaryAttr; + private List listAttr; + + private Map mapAttr1; + private Map> mapAttr2; + private Map>>> mapAttr3; + + public String getHashKey() { + return hashKey; + } + + public void setHashKey(String hashKey) { + this.hashKey = hashKey; + } + + public String getStringAttr() { + return stringAttr; + } + + public void setStringAttr(String stringAttr) { + this.stringAttr = stringAttr; + } + + public SdkBytes getBinaryAttr() { + return binaryAttr; + } + + public void setBinaryAttr(SdkBytes binaryAttr) { + this.binaryAttr = binaryAttr; + } + + public List getListAttr() { + return listAttr; + } + + public void setListAttr(List listAttr) { + this.listAttr = listAttr; + } + + public Map getMapAttr1() { + return mapAttr1; + } + + public void setMapAttr1(Map mapAttr1) { + this.mapAttr1 = mapAttr1; + } + + public Map> getMapAttr2() { + return mapAttr2; + } + + public void setMapAttr2(Map> mapAttr2) { + this.mapAttr2 = mapAttr2; + } + + public Map>>> getMapAttr3() { + return mapAttr3; + } + + public void setMapAttr3(Map>>> mapAttr3) { + this.mapAttr3 = mapAttr3; + } + } + + public static class HugeBeanFlat { + private String stringAttr1; + private String stringAttr2; + private String stringAttr3; + private String stringAttr4; + private String stringAttr5; + private String stringAttr6; + private String stringAttr7; + private String stringAttr8; + private String stringAttr9; + private String stringAttr10; + private String stringAttr11; + private String stringAttr12; + private String stringAttr13; + private String stringAttr14; + private String stringAttr15; + private String stringAttr16; + private String stringAttr17; + private String stringAttr18; + private String stringAttr19; + private String stringAttr20; + private String stringAttr21; + private String stringAttr22; + private String stringAttr23; + private String stringAttr24; + private String stringAttr25; + private String stringAttr26; + private String stringAttr27; + private String stringAttr28; + private String stringAttr29; + private String stringAttr30; + private String stringAttr31; + private String stringAttr32; + private String stringAttr33; + private String stringAttr34; + private String stringAttr35; + private String stringAttr36; + private String stringAttr37; + private String stringAttr38; + private String stringAttr39; + private String stringAttr40; + private String stringAttr41; + private String stringAttr42; + private String stringAttr43; + private String stringAttr44; + private String stringAttr45; + private String stringAttr46; + private String stringAttr47; + private String stringAttr48; + private String stringAttr49; + private String stringAttr50; + private String stringAttr51; + private String stringAttr52; + private String stringAttr53; + private String stringAttr54; + private String stringAttr55; + private String stringAttr56; + private String stringAttr57; + private String stringAttr58; + private String stringAttr59; + private String stringAttr60; + private String stringAttr61; + private String stringAttr62; + private String stringAttr63; + + public String getStringAttr1() { + return stringAttr1; + } + + public void setStringAttr1(String stringAttr1) { + this.stringAttr1 = stringAttr1; + } + + public String getStringAttr2() { + return stringAttr2; + } + + public void setStringAttr2(String stringAttr2) { + this.stringAttr2 = stringAttr2; + } + + public String getStringAttr3() { + return stringAttr3; + } + + public void setStringAttr3(String stringAttr3) { + this.stringAttr3 = stringAttr3; + } + + public String getStringAttr4() { + return stringAttr4; + } + + public void setStringAttr4(String stringAttr4) { + this.stringAttr4 = stringAttr4; + } + + public String getStringAttr5() { + return stringAttr5; + } + + public void setStringAttr5(String stringAttr5) { + this.stringAttr5 = stringAttr5; + } + + public String getStringAttr6() { + return stringAttr6; + } + + public void setStringAttr6(String stringAttr6) { + this.stringAttr6 = stringAttr6; + } + + public String getStringAttr7() { + return stringAttr7; + } + + public void setStringAttr7(String stringAttr7) { + this.stringAttr7 = stringAttr7; + } + + public String getStringAttr8() { + return stringAttr8; + } + + public void setStringAttr8(String stringAttr8) { + this.stringAttr8 = stringAttr8; + } + + public String getStringAttr9() { + return stringAttr9; + } + + public void setStringAttr9(String stringAttr9) { + this.stringAttr9 = stringAttr9; + } + + public String getStringAttr10() { + return stringAttr10; + } + + public void setStringAttr10(String stringAttr10) { + this.stringAttr10 = stringAttr10; + } + + public String getStringAttr11() { + return stringAttr11; + } + + public void setStringAttr11(String stringAttr11) { + this.stringAttr11 = stringAttr11; + } + + public String getStringAttr12() { + return stringAttr12; + } + + public void setStringAttr12(String stringAttr12) { + this.stringAttr12 = stringAttr12; + } + + public String getStringAttr13() { + return stringAttr13; + } + + public void setStringAttr13(String stringAttr13) { + this.stringAttr13 = stringAttr13; + } + + public String getStringAttr14() { + return stringAttr14; + } + + public void setStringAttr14(String stringAttr14) { + this.stringAttr14 = stringAttr14; + } + + public String getStringAttr15() { + return stringAttr15; + } + + public void setStringAttr15(String stringAttr15) { + this.stringAttr15 = stringAttr15; + } + + public String getStringAttr16() { + return stringAttr16; + } + + public void setStringAttr16(String stringAttr16) { + this.stringAttr16 = stringAttr16; + } + + public String getStringAttr17() { + return stringAttr17; + } + + public void setStringAttr17(String stringAttr17) { + this.stringAttr17 = stringAttr17; + } + + public String getStringAttr18() { + return stringAttr18; + } + + public void setStringAttr18(String stringAttr18) { + this.stringAttr18 = stringAttr18; + } + + public String getStringAttr19() { + return stringAttr19; + } + + public void setStringAttr19(String stringAttr19) { + this.stringAttr19 = stringAttr19; + } + + public String getStringAttr20() { + return stringAttr20; + } + + public void setStringAttr20(String stringAttr20) { + this.stringAttr20 = stringAttr20; + } + + public String getStringAttr21() { + return stringAttr21; + } + + public void setStringAttr21(String stringAttr21) { + this.stringAttr21 = stringAttr21; + } + + public String getStringAttr22() { + return stringAttr22; + } + + public void setStringAttr22(String stringAttr22) { + this.stringAttr22 = stringAttr22; + } + + public String getStringAttr23() { + return stringAttr23; + } + + public void setStringAttr23(String stringAttr23) { + this.stringAttr23 = stringAttr23; + } + + public String getStringAttr24() { + return stringAttr24; + } + + public void setStringAttr24(String stringAttr24) { + this.stringAttr24 = stringAttr24; + } + + public String getStringAttr25() { + return stringAttr25; + } + + public void setStringAttr25(String stringAttr25) { + this.stringAttr25 = stringAttr25; + } + + public String getStringAttr26() { + return stringAttr26; + } + + public void setStringAttr26(String stringAttr26) { + this.stringAttr26 = stringAttr26; + } + + public String getStringAttr27() { + return stringAttr27; + } + + public void setStringAttr27(String stringAttr27) { + this.stringAttr27 = stringAttr27; + } + + public String getStringAttr28() { + return stringAttr28; + } + + public void setStringAttr28(String stringAttr28) { + this.stringAttr28 = stringAttr28; + } + + public String getStringAttr29() { + return stringAttr29; + } + + public void setStringAttr29(String stringAttr29) { + this.stringAttr29 = stringAttr29; + } + + public String getStringAttr30() { + return stringAttr30; + } + + public void setStringAttr30(String stringAttr30) { + this.stringAttr30 = stringAttr30; + } + + public String getStringAttr31() { + return stringAttr31; + } + + public void setStringAttr31(String stringAttr31) { + this.stringAttr31 = stringAttr31; + } + + public String getStringAttr32() { + return stringAttr32; + } + + public void setStringAttr32(String stringAttr32) { + this.stringAttr32 = stringAttr32; + } + + public String getStringAttr33() { + return stringAttr33; + } + + public void setStringAttr33(String stringAttr33) { + this.stringAttr33 = stringAttr33; + } + + public String getStringAttr34() { + return stringAttr34; + } + + public void setStringAttr34(String stringAttr34) { + this.stringAttr34 = stringAttr34; + } + + public String getStringAttr35() { + return stringAttr35; + } + + public void setStringAttr35(String stringAttr35) { + this.stringAttr35 = stringAttr35; + } + + public String getStringAttr36() { + return stringAttr36; + } + + public void setStringAttr36(String stringAttr36) { + this.stringAttr36 = stringAttr36; + } + + public String getStringAttr37() { + return stringAttr37; + } + + public void setStringAttr37(String stringAttr37) { + this.stringAttr37 = stringAttr37; + } + + public String getStringAttr38() { + return stringAttr38; + } + + public void setStringAttr38(String stringAttr38) { + this.stringAttr38 = stringAttr38; + } + + public String getStringAttr39() { + return stringAttr39; + } + + public void setStringAttr39(String stringAttr39) { + this.stringAttr39 = stringAttr39; + } + + public String getStringAttr40() { + return stringAttr40; + } + + public void setStringAttr40(String stringAttr40) { + this.stringAttr40 = stringAttr40; + } + + public String getStringAttr41() { + return stringAttr41; + } + + public void setStringAttr41(String stringAttr41) { + this.stringAttr41 = stringAttr41; + } + + public String getStringAttr42() { + return stringAttr42; + } + + public void setStringAttr42(String stringAttr42) { + this.stringAttr42 = stringAttr42; + } + + public String getStringAttr43() { + return stringAttr43; + } + + public void setStringAttr43(String stringAttr43) { + this.stringAttr43 = stringAttr43; + } + + public String getStringAttr44() { + return stringAttr44; + } + + public void setStringAttr44(String stringAttr44) { + this.stringAttr44 = stringAttr44; + } + + public String getStringAttr45() { + return stringAttr45; + } + + public void setStringAttr45(String stringAttr45) { + this.stringAttr45 = stringAttr45; + } + + public String getStringAttr46() { + return stringAttr46; + } + + public void setStringAttr46(String stringAttr46) { + this.stringAttr46 = stringAttr46; + } + + public String getStringAttr47() { + return stringAttr47; + } + + public void setStringAttr47(String stringAttr47) { + this.stringAttr47 = stringAttr47; + } + + public String getStringAttr48() { + return stringAttr48; + } + + public void setStringAttr48(String stringAttr48) { + this.stringAttr48 = stringAttr48; + } + + public String getStringAttr49() { + return stringAttr49; + } + + public void setStringAttr49(String stringAttr49) { + this.stringAttr49 = stringAttr49; + } + + public String getStringAttr50() { + return stringAttr50; + } + + public void setStringAttr50(String stringAttr50) { + this.stringAttr50 = stringAttr50; + } + + public String getStringAttr51() { + return stringAttr51; + } + + public void setStringAttr51(String stringAttr51) { + this.stringAttr51 = stringAttr51; + } + + public String getStringAttr52() { + return stringAttr52; + } + + public void setStringAttr52(String stringAttr52) { + this.stringAttr52 = stringAttr52; + } + + public String getStringAttr53() { + return stringAttr53; + } + + public void setStringAttr53(String stringAttr53) { + this.stringAttr53 = stringAttr53; + } + + public String getStringAttr54() { + return stringAttr54; + } + + public void setStringAttr54(String stringAttr54) { + this.stringAttr54 = stringAttr54; + } + + public String getStringAttr55() { + return stringAttr55; + } + + public void setStringAttr55(String stringAttr55) { + this.stringAttr55 = stringAttr55; + } + + public String getStringAttr56() { + return stringAttr56; + } + + public void setStringAttr56(String stringAttr56) { + this.stringAttr56 = stringAttr56; + } + + public String getStringAttr57() { + return stringAttr57; + } + + public void setStringAttr57(String stringAttr57) { + this.stringAttr57 = stringAttr57; + } + + public String getStringAttr58() { + return stringAttr58; + } + + public void setStringAttr58(String stringAttr58) { + this.stringAttr58 = stringAttr58; + } + + public String getStringAttr59() { + return stringAttr59; + } + + public void setStringAttr59(String stringAttr59) { + this.stringAttr59 = stringAttr59; + } + + public String getStringAttr60() { + return stringAttr60; + } + + public void setStringAttr60(String stringAttr60) { + this.stringAttr60 = stringAttr60; + } + + public String getStringAttr61() { + return stringAttr61; + } + + public void setStringAttr61(String stringAttr61) { + this.stringAttr61 = stringAttr61; + } + + public String getStringAttr62() { + return stringAttr62; + } + + public void setStringAttr62(String stringAttr62) { + this.stringAttr62 = stringAttr62; + } + + public String getStringAttr63() { + return stringAttr63; + } + + public void setStringAttr63(String stringAttr63) { + this.stringAttr63 = stringAttr63; + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1ItemFactory.java new file mode 100644 index 000000000000..0acac1290fab --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1ItemFactory.java @@ -0,0 +1,703 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; +import com.amazonaws.services.dynamodbv2.model.AttributeValue; +import com.amazonaws.util.ImmutableMapParameter; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import software.amazon.awssdk.core.SdkBytes; + +public final class V1ItemFactory extends ItemFactory { + public V1HugeBean v1hugeBean() { + return V1HugeBean.fromHugeBean(super.hugeBean()); + } + + public V1TinyBean v1TinyBean() { + return V1TinyBean.fromTinyBean(super.tinyBean()); + } + + public V1SmallBean v1SmallBean() { + return V1SmallBean.fromSmallBean(super.smallBean()); + } + + public V1HugeBeanFlat v1HugeBeanFlat() { + return V1HugeBeanFlat.fromHugeBeanFlat(super.hugeBeanFlat()); + } + + @Override + protected Map asItem(TinyBean b) { + ImmutableMapParameter.Builder builder = ImmutableMapParameter.builder(); + + builder.put("stringAttr", av(b.getStringAttr())); + + return builder.build(); + } + + @Override + protected Map asItem(SmallBean b) { + ImmutableMapParameter.Builder builder = ImmutableMapParameter.builder(); + + builder.put("stringAttr", av(b.getStringAttr())); + builder.put("binaryAttr", av(b.getBinaryAttr())); + + List listAttr = b.getListAttr().stream().map(this::av).collect(Collectors.toList()); + + builder.put("listAttr", av(listAttr)); + + return builder.build(); + } + + @Override + protected Map asItem(HugeBean b) { + ImmutableMapParameter.Builder builder = ImmutableMapParameter.builder(); + + builder.put("hashKey", av(b.getHashKey())); + builder.put("stringAttr", av(b.getStringAttr())); + builder.put("binaryAttr", av(b.getBinaryAttr())); + + List listAttr = b.getListAttr().stream().map(this::av).collect(Collectors.toList()); + + builder.put("listAttr", av(listAttr)); + + Map mapAttr1 = b.getMapAttr1().entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, + e -> av(e.getValue()))); + + builder.put("mapAttr1", av(mapAttr1)); + + + Map mapAttr2 = b.getMapAttr2().entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, + e -> av(e.getValue().stream().map(this::av).collect(Collectors.toList())))); + + builder.put("mapAttr2", av(mapAttr2)); + + Map mapAttr3 = b.getMapAttr3().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + e -> { + List>> value = e.getValue(); + AttributeValue valueAv = av(value.stream().map(m -> av(m.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + ee -> av(ee.getValue().stream().map(this::av).collect(Collectors.toList())))))) + .collect(Collectors.toList())); + return valueAv; + })); + + builder.put("mapAttr3", av(mapAttr3)); + + return builder.build(); + } + + @Override + protected AttributeValue av(String val) { + return new AttributeValue() + .withS(val); + } + + @Override + protected AttributeValue av(List val) { + return new AttributeValue() + .withL(val); + } + + @Override + protected AttributeValue av(Map val) { + return new AttributeValue() + .withM(val); + } + + @Override + protected AttributeValue av(SdkBytes val) { + return new AttributeValue() + .withB(val.asByteBuffer()); + } + + @DynamoDBTable(tableName = "V1TinyBean") + public static class V1TinyBean extends ItemFactory.TinyBean { + public V1TinyBean() { + } + + public V1TinyBean(String stringAttr) { + super.setStringAttr(stringAttr); + } + + @DynamoDBHashKey + @Override + public String getStringAttr() { + return super.getStringAttr(); + } + + private static V1TinyBean fromTinyBean(TinyBean tb) { + V1TinyBean b = new V1TinyBean(); + b.setStringAttr(tb.getStringAttr()); + return b; + } + } + + @DynamoDBTable(tableName = "V1SmallBean") + public static class V1SmallBean extends ItemFactory.SmallBean { + private ByteBuffer binaryAttr; + + public V1SmallBean() { + } + + public V1SmallBean(String stringAttr) { + super.setStringAttr(stringAttr); + } + + @DynamoDBHashKey + @Override + public String getStringAttr() { + return super.getStringAttr(); + } + + @DynamoDBAttribute(attributeName = "binaryAttr") + public ByteBuffer getBinaryAttrV1() { + return binaryAttr; + } + + @DynamoDBAttribute(attributeName = "binaryAttr") + public void setBinaryAttrV1(ByteBuffer binaryAttr) { + this.binaryAttr = binaryAttr; + } + + @DynamoDBAttribute + @Override + public List getListAttr() { + return super.getListAttr(); + } + + private static V1SmallBean fromSmallBean(SmallBean sb) { + V1SmallBean b = new V1SmallBean(); + b.setStringAttr(sb.getStringAttr()); + b.setBinaryAttrV1(sb.getBinaryAttr().asByteBuffer()); + b.setListAttr(sb.getListAttr()); + return b; + } + } + + @DynamoDBTable(tableName = "V1HugeBean") + public static class V1HugeBean extends ItemFactory.HugeBean { + private ByteBuffer binaryAttr; + private Map mapAttr1; + private Map> mapAttr2; + private Map>>> mapAttr3; + + public V1HugeBean() { + } + + public V1HugeBean(String stringAttr) { + super.setStringAttr(stringAttr); + } + + @DynamoDBHashKey + @Override + public String getStringAttr() { + return super.getStringAttr(); + } + + @DynamoDBAttribute + @Override + public String getHashKey() { + return super.getHashKey(); + } + + @DynamoDBAttribute(attributeName = "binaryAttr") + public ByteBuffer getBinaryAttrV1() { + return binaryAttr; + } + + @DynamoDBAttribute(attributeName = "binaryAttr") + public void setBinaryAttrV1(ByteBuffer binaryAttr) { + this.binaryAttr = binaryAttr; + } + + @DynamoDBAttribute + @Override + public List getListAttr() { + return super.getListAttr(); + } + + @DynamoDBAttribute(attributeName = "mapAttr1") + public Map getMapAttr1V1() { + return mapAttr1; + } + + @DynamoDBAttribute(attributeName = "mapAttr1") + public void setMapAttr1V1(Map mapAttr1) { + this.mapAttr1 = mapAttr1; + } + + @DynamoDBAttribute(attributeName = "mapAttr2") + public Map> getMapAttr2V1() { + return mapAttr2; + } + + @DynamoDBAttribute(attributeName = "mapAttr2") + public void setMapAttr2V1(Map> mapAttr2) { + this.mapAttr2 = mapAttr2; + } + + @DynamoDBAttribute(attributeName = "mapAttr3") + public Map>>> getMapAttr3V1() { + return mapAttr3; + } + + @DynamoDBAttribute(attributeName = "mapAttr3") + public void setMapAttr3V1(Map>>> mapAttr3) { + this.mapAttr3 = mapAttr3; + } + + private static V1HugeBean fromHugeBean(HugeBean hb) { + V1HugeBean b = new V1HugeBean(); + b.setHashKey(hb.getHashKey()); + b.setStringAttr(hb.getStringAttr()); + b.setBinaryAttrV1(hb.getBinaryAttr().asByteBuffer()); + b.setListAttr(hb.getListAttr()); + + b.setMapAttr1V1(hb.getMapAttr1() + .entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().asByteBuffer()))); + + b.setMapAttr2V1(hb.getMapAttr2() + .entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + e -> e.getValue().stream().map(SdkBytes::asByteBuffer).collect(Collectors.toList())))); + + Map>>> mapAttr3V1 = hb.getMapAttr3() + .entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().stream() + .map(m -> m.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, ee -> + ee.getValue().stream().map(SdkBytes::asByteBuffer).collect(Collectors.toList()) + ))) + .collect(Collectors.toList()))); + + b.setMapAttr3V1(mapAttr3V1); + + return b; + } + } + + @DynamoDBTable(tableName = "V1HugeBeanFlat") + public static class V1HugeBeanFlat extends HugeBeanFlat { + public V1HugeBeanFlat() { + } + + public V1HugeBeanFlat(String stringAttr) { + this.setStringAttr1(stringAttr); + } + + @DynamoDBAttribute(attributeName = "stringAttr1") + @DynamoDBHashKey + @Override + public String getStringAttr1() { + return super.getStringAttr1(); + } + + @DynamoDBAttribute(attributeName = "stringAttr2") + @Override + public String getStringAttr2() { + return super.getStringAttr2(); + } + + @DynamoDBAttribute(attributeName = "stringAttr3") + @Override + public String getStringAttr3() { + return super.getStringAttr3(); + } + + @DynamoDBAttribute(attributeName = "stringAttr4") + @Override + public String getStringAttr4() { + return super.getStringAttr4(); + } + + @DynamoDBAttribute(attributeName = "stringAttr5") + @Override + public String getStringAttr5() { + return super.getStringAttr5(); + } + + @DynamoDBAttribute(attributeName = "stringAttr6") + @Override + public String getStringAttr6() { + return super.getStringAttr6(); + } + + @DynamoDBAttribute(attributeName = "stringAttr7") + @Override + public String getStringAttr7() { + return super.getStringAttr7(); + } + + @DynamoDBAttribute(attributeName = "stringAttr8") + @Override + public String getStringAttr8() { + return super.getStringAttr8(); + } + + @DynamoDBAttribute(attributeName = "stringAttr9") + @Override + public String getStringAttr9() { + return super.getStringAttr9(); + } + + @DynamoDBAttribute(attributeName = "stringAttr10") + @Override + public String getStringAttr10() { + return super.getStringAttr10(); + } + + @DynamoDBAttribute(attributeName = "stringAttr11") + @Override + public String getStringAttr11() { + return super.getStringAttr11(); + } + + @DynamoDBAttribute(attributeName = "stringAttr12") + @Override + public String getStringAttr12() { + return super.getStringAttr12(); + } + + @DynamoDBAttribute(attributeName = "stringAttr13") + @Override + public String getStringAttr13() { + return super.getStringAttr13(); + } + + @DynamoDBAttribute(attributeName = "stringAttr14") + @Override + public String getStringAttr14() { + return super.getStringAttr14(); + } + + @DynamoDBAttribute(attributeName = "stringAttr15") + @Override + public String getStringAttr15() { + return super.getStringAttr15(); + } + + @DynamoDBAttribute(attributeName = "stringAttr16") + @Override + public String getStringAttr16() { + return super.getStringAttr16(); + } + + @DynamoDBAttribute(attributeName = "stringAttr17") + @Override + public String getStringAttr17() { + return super.getStringAttr17(); + } + + @DynamoDBAttribute(attributeName = "stringAttr18") + @Override + public String getStringAttr18() { + return super.getStringAttr18(); + } + + @DynamoDBAttribute(attributeName = "stringAttr19") + @Override + public String getStringAttr19() { + return super.getStringAttr19(); + } + + @DynamoDBAttribute(attributeName = "stringAttr20") + @Override + public String getStringAttr20() { + return super.getStringAttr20(); + } + + @DynamoDBAttribute(attributeName = "stringAttr21") + @Override + public String getStringAttr21() { + return super.getStringAttr21(); + } + + @DynamoDBAttribute(attributeName = "stringAttr22") + @Override + public String getStringAttr22() { + return super.getStringAttr22(); + } + + @DynamoDBAttribute(attributeName = "stringAttr23") + @Override + public String getStringAttr23() { + return super.getStringAttr23(); + } + + @DynamoDBAttribute(attributeName = "stringAttr24") + @Override + public String getStringAttr24() { + return super.getStringAttr24(); + } + + @DynamoDBAttribute(attributeName = "stringAttr25") + @Override + public String getStringAttr25() { + return super.getStringAttr25(); + } + + @DynamoDBAttribute(attributeName = "stringAttr26") + @Override + public String getStringAttr26() { + return super.getStringAttr26(); + } + + @DynamoDBAttribute(attributeName = "stringAttr27") + @Override + public String getStringAttr27() { + return super.getStringAttr27(); + } + + @DynamoDBAttribute(attributeName = "stringAttr28") + @Override + public String getStringAttr28() { + return super.getStringAttr28(); + } + + @DynamoDBAttribute(attributeName = "stringAttr29") + @Override + public String getStringAttr29() { + return super.getStringAttr29(); + } + + @DynamoDBAttribute(attributeName = "stringAttr30") + @Override + public String getStringAttr30() { + return super.getStringAttr30(); + } + + @DynamoDBAttribute(attributeName = "stringAttr31") + @Override + public String getStringAttr31() { + return super.getStringAttr31(); + } + + @DynamoDBAttribute(attributeName = "stringAttr32") + @Override + public String getStringAttr32() { + return super.getStringAttr32(); + } + + @DynamoDBAttribute(attributeName = "stringAttr33") + @Override + public String getStringAttr33() { + return super.getStringAttr33(); + } + + @DynamoDBAttribute(attributeName = "stringAttr34") + @Override + public String getStringAttr34() { + return super.getStringAttr34(); + } + + @DynamoDBAttribute(attributeName = "stringAttr35") + @Override + public String getStringAttr35() { + return super.getStringAttr35(); + } + + @DynamoDBAttribute(attributeName = "stringAttr36") + @Override + public String getStringAttr36() { + return super.getStringAttr36(); + } + + @DynamoDBAttribute(attributeName = "stringAttr37") + @Override + public String getStringAttr37() { + return super.getStringAttr37(); + } + + @DynamoDBAttribute(attributeName = "stringAttr38") + @Override + public String getStringAttr38() { + return super.getStringAttr38(); + } + + @DynamoDBAttribute(attributeName = "stringAttr39") + @Override + public String getStringAttr39() { + return super.getStringAttr39(); + } + + @DynamoDBAttribute(attributeName = "stringAttr40") + @Override + public String getStringAttr40() { + return super.getStringAttr40(); + } + + @DynamoDBAttribute(attributeName = "stringAttr41") + @Override + public String getStringAttr41() { + return super.getStringAttr41(); + } + + @DynamoDBAttribute(attributeName = "stringAttr42") + @Override + public String getStringAttr42() { + return super.getStringAttr42(); + } + + @DynamoDBAttribute(attributeName = "stringAttr43") + @Override + public String getStringAttr43() { + return super.getStringAttr43(); + } + + @DynamoDBAttribute(attributeName = "stringAttr44") + @Override + public String getStringAttr44() { + return super.getStringAttr44(); + } + + @DynamoDBAttribute(attributeName = "stringAttr45") + @Override + public String getStringAttr45() { + return super.getStringAttr45(); + } + + @DynamoDBAttribute(attributeName = "stringAttr46") + @Override + public String getStringAttr46() { + return super.getStringAttr46(); + } + + @DynamoDBAttribute(attributeName = "stringAttr47") + @Override + public String getStringAttr47() { + return super.getStringAttr47(); + } + + @DynamoDBAttribute(attributeName = "stringAttr48") + @Override + public String getStringAttr48() { + return super.getStringAttr48(); + } + + @DynamoDBAttribute(attributeName = "stringAttr49") + @Override + public String getStringAttr49() { + return super.getStringAttr49(); + } + + @DynamoDBAttribute(attributeName = "stringAttr50") + @Override + public String getStringAttr50() { + return super.getStringAttr50(); + } + + @DynamoDBAttribute(attributeName = "stringAttr51") + @Override + public String getStringAttr51() { + return super.getStringAttr51(); + } + + @DynamoDBAttribute(attributeName = "stringAttr52") + @Override + public String getStringAttr52() { + return super.getStringAttr52(); + } + + @DynamoDBAttribute(attributeName = "stringAttr53") + @Override + public String getStringAttr53() { + return super.getStringAttr53(); + } + + @Override + @DynamoDBAttribute(attributeName = "stringAttr54") + public String getStringAttr54() { + return super.getStringAttr54(); + } + + @DynamoDBAttribute(attributeName = "stringAttr55") + @Override + public String getStringAttr55() { + return super.getStringAttr55(); + } + + @DynamoDBAttribute(attributeName = "stringAttr56") + @Override + public String getStringAttr56() { + return super.getStringAttr56(); + } + + @DynamoDBAttribute(attributeName = "stringAttr57") + @Override + public String getStringAttr57() { + return super.getStringAttr57(); + } + + @DynamoDBAttribute(attributeName = "stringAttr58") + @Override + public String getStringAttr58() { + return super.getStringAttr58(); + } + + @DynamoDBAttribute(attributeName = "stringAttr59") + @Override + public String getStringAttr59() { + return super.getStringAttr59(); + } + + @DynamoDBAttribute(attributeName = "stringAttr60") + @Override + public String getStringAttr60() { + return super.getStringAttr60(); + } + + @DynamoDBAttribute(attributeName = "stringAttr61") + @Override + public String getStringAttr61() { + return super.getStringAttr61(); + } + + @DynamoDBAttribute(attributeName = "stringAttr62") + @Override + public String getStringAttr62() { + return super.getStringAttr62(); + } + + @DynamoDBAttribute(attributeName = "stringAttr63") + @Override + public String getStringAttr63() { + return super.getStringAttr63(); + } + + public static V1HugeBeanFlat fromHugeBeanFlat(HugeBeanFlat b) { + V1HugeBeanFlat bean = new V1HugeBeanFlat(); + for (int i = 1; i <= 63; ++i) { + try { + Method setter = V1HugeBeanFlat.class.getMethod("setStringAttr" + i, String.class); + Method getter = HugeBeanFlat.class.getMethod("getStringAttr" + i); + setter.setAccessible(true); + setter.invoke(bean, getter.invoke(b)); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + return bean; + } + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbBaseClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbBaseClient.java new file mode 100644 index 000000000000..b3cbb5213b05 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbBaseClient.java @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.AbstractAmazonDynamoDB; +import org.openjdk.jmh.infra.Blackhole; + +abstract class V1TestDynamoDbBaseClient extends AbstractAmazonDynamoDB { + protected final Blackhole bh; + + protected V1TestDynamoDbBaseClient(Blackhole bh) { + this.bh = bh; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbDeleteItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbDeleteItemClient.java new file mode 100644 index 000000000000..cafcfdcdff47 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbDeleteItemClient.java @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest; +import com.amazonaws.services.dynamodbv2.model.DeleteItemResult; +import org.openjdk.jmh.infra.Blackhole; + +public class V1TestDynamoDbDeleteItemClient extends V1TestDynamoDbBaseClient { + private static final DeleteItemResult DELETE_ITEM_RESULT = new DeleteItemResult(); + + public V1TestDynamoDbDeleteItemClient(Blackhole bh) { + super(bh); + } + + @Override + public DeleteItemResult deleteItem(DeleteItemRequest request) { + bh.consume(request); + return DELETE_ITEM_RESULT; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbGetItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbGetItemClient.java new file mode 100644 index 000000000000..a2994ae9bb8b --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbGetItemClient.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.model.GetItemRequest; +import com.amazonaws.services.dynamodbv2.model.GetItemResult; +import org.openjdk.jmh.infra.Blackhole; + +public class V1TestDynamoDbGetItemClient extends V1TestDynamoDbBaseClient { + private final GetItemResult getItemResult; + + public V1TestDynamoDbGetItemClient(Blackhole bh, GetItemResult getItemResult) { + super(bh); + this.getItemResult = getItemResult; + } + + @Override + public GetItemResult getItem(GetItemRequest request) { + bh.consume(request); + return getItemResult; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbPutItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbPutItemClient.java new file mode 100644 index 000000000000..3cc51c551ce3 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbPutItemClient.java @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.model.PutItemRequest; +import com.amazonaws.services.dynamodbv2.model.PutItemResult; +import org.openjdk.jmh.infra.Blackhole; + +public class V1TestDynamoDbPutItemClient extends V1TestDynamoDbBaseClient { + private static final PutItemResult PUT_ITEM_RESULT = new PutItemResult(); + + public V1TestDynamoDbPutItemClient(Blackhole bh) { + super(bh); + } + + @Override + public PutItemResult putItem(PutItemRequest request) { + bh.consume(request); + return PUT_ITEM_RESULT; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbQueryClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbQueryClient.java new file mode 100644 index 000000000000..b64a18edbcd0 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbQueryClient.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.model.QueryRequest; +import com.amazonaws.services.dynamodbv2.model.QueryResult; +import org.openjdk.jmh.infra.Blackhole; + +public class V1TestDynamoDbQueryClient extends V1TestDynamoDbBaseClient { + private final QueryResult queryResult; + + public V1TestDynamoDbQueryClient(Blackhole bh, QueryResult queryResult) { + super(bh); + this.queryResult = queryResult; + } + + @Override + public QueryResult query(QueryRequest request) { + bh.consume(request); + return queryResult; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbScanClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbScanClient.java new file mode 100644 index 000000000000..c7f4370d8b3e --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbScanClient.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.model.ScanRequest; +import com.amazonaws.services.dynamodbv2.model.ScanResult; +import org.openjdk.jmh.infra.Blackhole; + +public class V1TestDynamoDbScanClient extends V1TestDynamoDbBaseClient { + private final ScanResult scanResult; + + public V1TestDynamoDbScanClient(Blackhole bh, ScanResult scanResult) { + super(bh); + this.scanResult = scanResult; + } + + @Override + public ScanResult scan(ScanRequest request) { + bh.consume(request); + return scanResult; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbUpdateItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbUpdateItemClient.java new file mode 100644 index 000000000000..ded1121b3060 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V1TestDynamoDbUpdateItemClient.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import com.amazonaws.services.dynamodbv2.model.UpdateItemRequest; +import com.amazonaws.services.dynamodbv2.model.UpdateItemResult; +import org.openjdk.jmh.infra.Blackhole; + +public class V1TestDynamoDbUpdateItemClient extends V1TestDynamoDbBaseClient { + private final UpdateItemResult updateItemResult; + + public V1TestDynamoDbUpdateItemClient(Blackhole bh, UpdateItemResult updateItemResult) { + super(bh); + this.updateItemResult = updateItemResult; + } + + @Override + public UpdateItemResult updateItem(UpdateItemRequest request) { + bh.consume(request); + return updateItemResult; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2ItemFactory.java new file mode 100644 index 000000000000..ab3ec136d9c1 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2ItemFactory.java @@ -0,0 +1,355 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import static software.amazon.awssdk.enhanced.dynamodb.mapper.StaticAttributeTags.primaryPartitionKey; + +import com.amazonaws.util.ImmutableMapParameter; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.enhanced.dynamodb.EnhancedType; +import software.amazon.awssdk.enhanced.dynamodb.TableSchema; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; + +public final class V2ItemFactory extends ItemFactory { + public static final TableSchema TINY_BEAN_TABLE_SCHEMA = + TableSchema.builder(ItemFactory.TinyBean.class) + .newItemSupplier(ItemFactory.TinyBean::new) + .addAttribute(String.class, a -> a.name("stringAttr") + .getter(ItemFactory.TinyBean::getStringAttr) + .setter(ItemFactory.TinyBean::setStringAttr) + .tags(primaryPartitionKey())) + .build(); + + public static final TableSchema SMALL_BEAN_TABLE_SCHEMA = + TableSchema.builder(ItemFactory.SmallBean.class) + .newItemSupplier(ItemFactory.SmallBean::new) + .addAttribute(String.class, a -> a.name("stringAttr") + .getter(ItemFactory.SmallBean::getStringAttr) + .setter(ItemFactory.SmallBean::setStringAttr) + .tags(primaryPartitionKey())) + .addAttribute(SdkBytes.class, a -> a.name("binaryAttr") + .getter(ItemFactory.SmallBean::getBinaryAttr) + .setter(ItemFactory.SmallBean::setBinaryAttr)) + .addAttribute(EnhancedType.listOf(String.class), a -> a.name("listAttr") + .getter(ItemFactory.SmallBean::getListAttr) + .setter(ItemFactory.SmallBean::setListAttr)) + .build(); + + public static final TableSchema HUGE_BEAN_TABLE_SCHEMA = + TableSchema.builder(ItemFactory.HugeBean.class) + .newItemSupplier(ItemFactory.HugeBean::new) + .addAttribute(String.class, a -> a.name("stringAttr") + .getter(ItemFactory.HugeBean::getStringAttr) + .setter(ItemFactory.HugeBean::setStringAttr) + .tags(primaryPartitionKey())) + .addAttribute(String.class, a -> a.name("hashKey") + .getter(ItemFactory.HugeBean::getHashKey) + .setter(ItemFactory.HugeBean::setHashKey)) + .addAttribute(SdkBytes.class, a -> a.name("binaryAttr") + .getter(ItemFactory.HugeBean::getBinaryAttr) + .setter(ItemFactory.HugeBean::setBinaryAttr)) + .addAttribute(EnhancedType.listOf(String.class), a -> a.name("listAttr") + .getter(ItemFactory.HugeBean::getListAttr) + .setter(ItemFactory.HugeBean::setListAttr)) + .addAttribute(new EnhancedType>() { + }, a -> a.name("mapAttr1") + .getter(ItemFactory.HugeBean::getMapAttr1) + .setter(ItemFactory.HugeBean::setMapAttr1)) + .addAttribute(new EnhancedType>>() { + }, a -> a.name("mapAttr2") + .getter(ItemFactory.HugeBean::getMapAttr2) + .setter(ItemFactory.HugeBean::setMapAttr2)) + .addAttribute(new EnhancedType>>>>() { + }, a -> a.name("mapAttr3") + .getter(ItemFactory.HugeBean::getMapAttr3) + .setter(ItemFactory.HugeBean::setMapAttr3)) + .build(); + + public static final TableSchema HUGE_BEAN_FLAT_TABLE_SCHEMA = + TableSchema.builder(ItemFactory.HugeBeanFlat.class) + .newItemSupplier(ItemFactory.HugeBeanFlat::new) + .addAttribute(String.class, a -> a.name("stringAttr") + .getter(ItemFactory.HugeBeanFlat::getStringAttr1) + .setter(ItemFactory.HugeBeanFlat::setStringAttr1) + .tags(primaryPartitionKey())) + .addAttribute(String.class, a -> a.name("stringAttr2") + .getter(ItemFactory.HugeBeanFlat::getStringAttr2) + .setter(ItemFactory.HugeBeanFlat::setStringAttr2)) + .addAttribute(String.class, a -> a.name("stringAttr3") + .getter(ItemFactory.HugeBeanFlat::getStringAttr3) + .setter(ItemFactory.HugeBeanFlat::setStringAttr3)) + .addAttribute(String.class, a -> a.name("stringAttr4") + .getter(ItemFactory.HugeBeanFlat::getStringAttr4) + .setter(ItemFactory.HugeBeanFlat::setStringAttr4)) + .addAttribute(String.class, a -> a.name("stringAttr5") + .getter(ItemFactory.HugeBeanFlat::getStringAttr5) + .setter(ItemFactory.HugeBeanFlat::setStringAttr5)) + .addAttribute(String.class, a -> a.name("stringAttr6") + .getter(ItemFactory.HugeBeanFlat::getStringAttr6) + .setter(ItemFactory.HugeBeanFlat::setStringAttr6)) + .addAttribute(String.class, a -> a.name("stringAttr7") + .getter(ItemFactory.HugeBeanFlat::getStringAttr7) + .setter(ItemFactory.HugeBeanFlat::setStringAttr7)) + .addAttribute(String.class, a -> a.name("stringAttr8") + .getter(ItemFactory.HugeBeanFlat::getStringAttr8) + .setter(ItemFactory.HugeBeanFlat::setStringAttr8)) + .addAttribute(String.class, a -> a.name("stringAttr9") + .getter(ItemFactory.HugeBeanFlat::getStringAttr9) + .setter(ItemFactory.HugeBeanFlat::setStringAttr9)) + .addAttribute(String.class, a -> a.name("stringAttr10") + .getter(ItemFactory.HugeBeanFlat::getStringAttr10) + .setter(ItemFactory.HugeBeanFlat::setStringAttr10)) + .addAttribute(String.class, a -> a.name("stringAttr11") + .getter(ItemFactory.HugeBeanFlat::getStringAttr11) + .setter(ItemFactory.HugeBeanFlat::setStringAttr11)) + .addAttribute(String.class, a -> a.name("stringAttr12") + .getter(ItemFactory.HugeBeanFlat::getStringAttr12) + .setter(ItemFactory.HugeBeanFlat::setStringAttr12)) + .addAttribute(String.class, a -> a.name("stringAttr13") + .getter(ItemFactory.HugeBeanFlat::getStringAttr13) + .setter(ItemFactory.HugeBeanFlat::setStringAttr13)) + .addAttribute(String.class, a -> a.name("stringAttr14") + .getter(ItemFactory.HugeBeanFlat::getStringAttr14) + .setter(ItemFactory.HugeBeanFlat::setStringAttr14)) + .addAttribute(String.class, a -> a.name("stringAttr15") + .getter(ItemFactory.HugeBeanFlat::getStringAttr15) + .setter(ItemFactory.HugeBeanFlat::setStringAttr15)) + .addAttribute(String.class, a -> a.name("stringAttr16") + .getter(ItemFactory.HugeBeanFlat::getStringAttr16) + .setter(ItemFactory.HugeBeanFlat::setStringAttr16)) + .addAttribute(String.class, a -> a.name("stringAttr17") + .getter(ItemFactory.HugeBeanFlat::getStringAttr17) + .setter(ItemFactory.HugeBeanFlat::setStringAttr17)) + .addAttribute(String.class, a -> a.name("stringAttr18") + .getter(ItemFactory.HugeBeanFlat::getStringAttr18) + .setter(ItemFactory.HugeBeanFlat::setStringAttr18)) + .addAttribute(String.class, a -> a.name("stringAttr19") + .getter(ItemFactory.HugeBeanFlat::getStringAttr19) + .setter(ItemFactory.HugeBeanFlat::setStringAttr19)) + .addAttribute(String.class, a -> a.name("stringAttr20") + .getter(ItemFactory.HugeBeanFlat::getStringAttr20) + .setter(ItemFactory.HugeBeanFlat::setStringAttr20)) + .addAttribute(String.class, a -> a.name("stringAttr21") + .getter(ItemFactory.HugeBeanFlat::getStringAttr21) + .setter(ItemFactory.HugeBeanFlat::setStringAttr21)) + .addAttribute(String.class, a -> a.name("stringAttr22") + .getter(ItemFactory.HugeBeanFlat::getStringAttr22) + .setter(ItemFactory.HugeBeanFlat::setStringAttr22)) + .addAttribute(String.class, a -> a.name("stringAttr23") + .getter(ItemFactory.HugeBeanFlat::getStringAttr23) + .setter(ItemFactory.HugeBeanFlat::setStringAttr23)) + .addAttribute(String.class, a -> a.name("stringAttr24") + .getter(ItemFactory.HugeBeanFlat::getStringAttr24) + .setter(ItemFactory.HugeBeanFlat::setStringAttr24)) + .addAttribute(String.class, a -> a.name("stringAttr25") + .getter(ItemFactory.HugeBeanFlat::getStringAttr25) + .setter(ItemFactory.HugeBeanFlat::setStringAttr25)) + .addAttribute(String.class, a -> a.name("stringAttr26") + .getter(ItemFactory.HugeBeanFlat::getStringAttr26) + .setter(ItemFactory.HugeBeanFlat::setStringAttr26)) + .addAttribute(String.class, a -> a.name("stringAttr27") + .getter(ItemFactory.HugeBeanFlat::getStringAttr27) + .setter(ItemFactory.HugeBeanFlat::setStringAttr27)) + .addAttribute(String.class, a -> a.name("stringAttr28") + .getter(ItemFactory.HugeBeanFlat::getStringAttr28) + .setter(ItemFactory.HugeBeanFlat::setStringAttr28)) + .addAttribute(String.class, a -> a.name("stringAttr29") + .getter(ItemFactory.HugeBeanFlat::getStringAttr29) + .setter(ItemFactory.HugeBeanFlat::setStringAttr29)) + .addAttribute(String.class, a -> a.name("stringAttr30") + .getter(ItemFactory.HugeBeanFlat::getStringAttr30) + .setter(ItemFactory.HugeBeanFlat::setStringAttr30)) + .addAttribute(String.class, a -> a.name("stringAttr31") + .getter(ItemFactory.HugeBeanFlat::getStringAttr31) + .setter(ItemFactory.HugeBeanFlat::setStringAttr31)) + .addAttribute(String.class, a -> a.name("stringAttr32") + .getter(ItemFactory.HugeBeanFlat::getStringAttr32) + .setter(ItemFactory.HugeBeanFlat::setStringAttr32)) + .addAttribute(String.class, a -> a.name("stringAttr33") + .getter(ItemFactory.HugeBeanFlat::getStringAttr33) + .setter(ItemFactory.HugeBeanFlat::setStringAttr33)) + .addAttribute(String.class, a -> a.name("stringAttr34") + .getter(ItemFactory.HugeBeanFlat::getStringAttr34) + .setter(ItemFactory.HugeBeanFlat::setStringAttr34)) + .addAttribute(String.class, a -> a.name("stringAttr35") + .getter(ItemFactory.HugeBeanFlat::getStringAttr35) + .setter(ItemFactory.HugeBeanFlat::setStringAttr35)) + .addAttribute(String.class, a -> a.name("stringAttr36") + .getter(ItemFactory.HugeBeanFlat::getStringAttr36) + .setter(ItemFactory.HugeBeanFlat::setStringAttr36)) + .addAttribute(String.class, a -> a.name("stringAttr37") + .getter(ItemFactory.HugeBeanFlat::getStringAttr37) + .setter(ItemFactory.HugeBeanFlat::setStringAttr37)) + .addAttribute(String.class, a -> a.name("stringAttr38") + .getter(ItemFactory.HugeBeanFlat::getStringAttr38) + .setter(ItemFactory.HugeBeanFlat::setStringAttr38)) + .addAttribute(String.class, a -> a.name("stringAttr39") + .getter(ItemFactory.HugeBeanFlat::getStringAttr39) + .setter(ItemFactory.HugeBeanFlat::setStringAttr39)) + .addAttribute(String.class, a -> a.name("stringAttr40") + .getter(ItemFactory.HugeBeanFlat::getStringAttr40) + .setter(ItemFactory.HugeBeanFlat::setStringAttr40)) + .addAttribute(String.class, a -> a.name("stringAttr41") + .getter(ItemFactory.HugeBeanFlat::getStringAttr41) + .setter(ItemFactory.HugeBeanFlat::setStringAttr41)) + .addAttribute(String.class, a -> a.name("stringAttr42") + .getter(ItemFactory.HugeBeanFlat::getStringAttr42) + .setter(ItemFactory.HugeBeanFlat::setStringAttr42)) + .addAttribute(String.class, a -> a.name("stringAttr43") + .getter(ItemFactory.HugeBeanFlat::getStringAttr43) + .setter(ItemFactory.HugeBeanFlat::setStringAttr43)) + .addAttribute(String.class, a -> a.name("stringAttr44") + .getter(ItemFactory.HugeBeanFlat::getStringAttr44) + .setter(ItemFactory.HugeBeanFlat::setStringAttr44)) + .addAttribute(String.class, a -> a.name("stringAttr45") + .getter(ItemFactory.HugeBeanFlat::getStringAttr45) + .setter(ItemFactory.HugeBeanFlat::setStringAttr45)) + .addAttribute(String.class, a -> a.name("stringAttr46") + .getter(ItemFactory.HugeBeanFlat::getStringAttr46) + .setter(ItemFactory.HugeBeanFlat::setStringAttr46)) + .addAttribute(String.class, a -> a.name("stringAttr47") + .getter(ItemFactory.HugeBeanFlat::getStringAttr47) + .setter(ItemFactory.HugeBeanFlat::setStringAttr47)) + .addAttribute(String.class, a -> a.name("stringAttr48") + .getter(ItemFactory.HugeBeanFlat::getStringAttr48) + .setter(ItemFactory.HugeBeanFlat::setStringAttr48)) + .addAttribute(String.class, a -> a.name("stringAttr49") + .getter(ItemFactory.HugeBeanFlat::getStringAttr49) + .setter(ItemFactory.HugeBeanFlat::setStringAttr49)) + .addAttribute(String.class, a -> a.name("stringAttr50") + .getter(ItemFactory.HugeBeanFlat::getStringAttr50) + .setter(ItemFactory.HugeBeanFlat::setStringAttr50)) + .addAttribute(String.class, a -> a.name("stringAttr51") + .getter(ItemFactory.HugeBeanFlat::getStringAttr51) + .setter(ItemFactory.HugeBeanFlat::setStringAttr51)) + .addAttribute(String.class, a -> a.name("stringAttr52") + .getter(ItemFactory.HugeBeanFlat::getStringAttr52) + .setter(ItemFactory.HugeBeanFlat::setStringAttr52)) + .addAttribute(String.class, a -> a.name("stringAttr53") + .getter(ItemFactory.HugeBeanFlat::getStringAttr53) + .setter(ItemFactory.HugeBeanFlat::setStringAttr53)) + .addAttribute(String.class, a -> a.name("stringAttr54") + .getter(ItemFactory.HugeBeanFlat::getStringAttr54) + .setter(ItemFactory.HugeBeanFlat::setStringAttr54)) + .addAttribute(String.class, a -> a.name("stringAttr55") + .getter(ItemFactory.HugeBeanFlat::getStringAttr55) + .setter(ItemFactory.HugeBeanFlat::setStringAttr55)) + .addAttribute(String.class, a -> a.name("stringAttr56") + .getter(ItemFactory.HugeBeanFlat::getStringAttr56) + .setter(ItemFactory.HugeBeanFlat::setStringAttr56)) + .addAttribute(String.class, a -> a.name("stringAttr57") + .getter(ItemFactory.HugeBeanFlat::getStringAttr57) + .setter(ItemFactory.HugeBeanFlat::setStringAttr57)) + .addAttribute(String.class, a -> a.name("stringAttr58") + .getter(ItemFactory.HugeBeanFlat::getStringAttr58) + .setter(ItemFactory.HugeBeanFlat::setStringAttr58)) + .addAttribute(String.class, a -> a.name("stringAttr59") + .getter(ItemFactory.HugeBeanFlat::getStringAttr59) + .setter(ItemFactory.HugeBeanFlat::setStringAttr59)) + .addAttribute(String.class, a -> a.name("stringAttr60") + .getter(ItemFactory.HugeBeanFlat::getStringAttr60) + .setter(ItemFactory.HugeBeanFlat::setStringAttr60)) + .addAttribute(String.class, a -> a.name("stringAttr61") + .getter(ItemFactory.HugeBeanFlat::getStringAttr61) + .setter(ItemFactory.HugeBeanFlat::setStringAttr61)) + .addAttribute(String.class, a -> a.name("stringAttr62") + .getter(ItemFactory.HugeBeanFlat::getStringAttr62) + .setter(ItemFactory.HugeBeanFlat::setStringAttr62)) + .addAttribute(String.class, a -> a.name("stringAttr63") + .getter(ItemFactory.HugeBeanFlat::getStringAttr63) + .setter(ItemFactory.HugeBeanFlat::setStringAttr63)) + .build(); + + + protected Map asItem(TinyBean b) { + ImmutableMapParameter.Builder builder = ImmutableMapParameter.builder(); + + builder.put("stringAttr", av(b.getStringAttr())); + + return builder.build(); + } + + protected Map asItem(SmallBean b) { + ImmutableMapParameter.Builder builder = ImmutableMapParameter.builder(); + + builder.put("stringAttr", av(b.getStringAttr())); + builder.put("binaryAttr", av(b.getBinaryAttr())); + + List listAttr = b.getListAttr().stream().map(this::av).collect(Collectors.toList()); + + builder.put("listAttr", av(listAttr)); + + return builder.build(); + } + + protected Map asItem(HugeBean b) { + ImmutableMapParameter.Builder builder = ImmutableMapParameter.builder(); + + builder.put("hashKey", av(b.getHashKey())); + builder.put("stringAttr", av(b.getStringAttr())); + builder.put("binaryAttr", av(b.getBinaryAttr())); + + List listAttr = b.getListAttr().stream().map(this::av).collect(Collectors.toList()); + + builder.put("listAttr", av(listAttr)); + + Map mapAttr1 = b.getMapAttr1().entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, + e -> av(e.getValue()))); + + builder.put("mapAttr1", av(mapAttr1)); + + + Map mapAttr2 = b.getMapAttr2().entrySet().stream().collect( + Collectors.toMap(Map.Entry::getKey, + e -> av(e.getValue().stream().map(this::av).collect(Collectors.toList())))); + + builder.put("mapAttr2", av(mapAttr2)); + + Map mapAttr3 = b.getMapAttr3().entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + e -> { + List>> value = e.getValue(); + AttributeValue valueAv = av(value.stream().map(m -> av(m.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, + ee -> av(ee.getValue().stream().map(this::av).collect(Collectors.toList())))))) + .collect(Collectors.toList())); + return valueAv; + })); + + builder.put("mapAttr3", av(mapAttr3)); + + return builder.build(); + } + + protected AttributeValue av(String val) { + return AttributeValue.builder().s(val).build(); + } + + protected AttributeValue av(List val) { + return AttributeValue.builder().l(val).build(); + } + + protected AttributeValue av(Map val) { + return AttributeValue.builder().m(val).build(); + } + + protected AttributeValue av(SdkBytes val) { + return AttributeValue.builder().b(val).build(); + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbBaseClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbBaseClient.java new file mode 100644 index 000000000000..f05ab204f49d --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbBaseClient.java @@ -0,0 +1,36 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; + +abstract class V2TestDynamoDbBaseClient implements DynamoDbClient { + protected final Blackhole bh; + + protected V2TestDynamoDbBaseClient(Blackhole bh) { + this.bh = bh; + } + + @Override + public String serviceName() { + return "DynamoDB"; + } + + @Override + public void close() { + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbDeleteItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbDeleteItemClient.java new file mode 100644 index 000000000000..8694e79a5bff --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbDeleteItemClient.java @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest; +import software.amazon.awssdk.services.dynamodb.model.DeleteItemResponse; + +public final class V2TestDynamoDbDeleteItemClient extends V2TestDynamoDbBaseClient { + private static final DeleteItemResponse DELETE_ITEM_RESPONSE = DeleteItemResponse.builder().build(); + + public V2TestDynamoDbDeleteItemClient(Blackhole bh) { + super(bh); + } + + @Override + public DeleteItemResponse deleteItem(DeleteItemRequest deleteItemRequest) { + bh.consume(deleteItemRequest); + return DELETE_ITEM_RESPONSE; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbGetItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbGetItemClient.java new file mode 100644 index 000000000000..2017d876d4e7 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbGetItemClient.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest; +import software.amazon.awssdk.services.dynamodb.model.GetItemResponse; + +public final class V2TestDynamoDbGetItemClient extends V2TestDynamoDbBaseClient { + private final GetItemResponse getItemResponse; + + public V2TestDynamoDbGetItemClient(Blackhole bh, GetItemResponse getItemResponse) { + super(bh); + this.getItemResponse = getItemResponse; + } + + @Override + public GetItemResponse getItem(GetItemRequest getItemRequest) { + bh.consume(getItemRequest); + return getItemResponse; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbPutItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbPutItemClient.java new file mode 100644 index 000000000000..5b2d611f641b --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbPutItemClient.java @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; + +public final class V2TestDynamoDbPutItemClient extends V2TestDynamoDbBaseClient { + private static final PutItemResponse PUT_ITEM_RESPONSE = PutItemResponse.builder().build(); + + public V2TestDynamoDbPutItemClient(Blackhole bh) { + super(bh); + } + + @Override + public PutItemResponse putItem(PutItemRequest putItemRequest) { + bh.consume(putItemRequest); + return PUT_ITEM_RESPONSE; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbQueryClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbQueryClient.java new file mode 100644 index 000000000000..c925c50a490c --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbQueryClient.java @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.model.QueryRequest; +import software.amazon.awssdk.services.dynamodb.model.QueryResponse; +import software.amazon.awssdk.services.dynamodb.paginators.QueryIterable; + +public final class V2TestDynamoDbQueryClient extends V2TestDynamoDbBaseClient { + private final QueryResponse queryResponse; + + public V2TestDynamoDbQueryClient(Blackhole bh, QueryResponse queryResponse) { + super(bh); + this.queryResponse = queryResponse; + } + + @Override + public QueryResponse query(QueryRequest queryRequest) { + bh.consume(queryRequest); + return this.queryResponse; + } + + @Override + public QueryIterable queryPaginator(QueryRequest queryRequest) { + return new QueryIterable(this, queryRequest); + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbScanClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbScanClient.java new file mode 100644 index 000000000000..c6d4a4d40bc5 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbScanClient.java @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.model.ScanRequest; +import software.amazon.awssdk.services.dynamodb.model.ScanResponse; +import software.amazon.awssdk.services.dynamodb.paginators.ScanIterable; + +public final class V2TestDynamoDbScanClient extends V2TestDynamoDbBaseClient { + private final ScanResponse scanResponse; + + public V2TestDynamoDbScanClient(Blackhole bh, ScanResponse scanResponse) { + super(bh); + this.scanResponse = scanResponse; + } + + @Override + public ScanResponse scan(ScanRequest scanRequest) { + bh.consume(scanRequest); + return this.scanResponse; + } + + @Override + public ScanIterable scanPaginator(ScanRequest scanRequest) { + return new ScanIterable(this, scanRequest); + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbUpdateItemClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbUpdateItemClient.java new file mode 100644 index 000000000000..77a9238240ed --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb/V2TestDynamoDbUpdateItemClient.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.benchmark.enhanced.dynamodb; + +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest; +import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse; + +public final class V2TestDynamoDbUpdateItemClient extends V2TestDynamoDbBaseClient { + private final UpdateItemResponse updateItemResponse; + + public V2TestDynamoDbUpdateItemClient(Blackhole bh, UpdateItemResponse updateItemResponse) { + super(bh); + this.updateItemResponse = updateItemResponse; + } + + @Override + public UpdateItemResponse updateItem(UpdateItemRequest updateItemRequest) { + bh.consume(updateItemRequest); + return this.updateItemResponse; + } +} diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/AbstractItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/AbstractItemFactory.java index a3805803ff52..d6ff2f869ccb 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/AbstractItemFactory.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/AbstractItemFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1DynamoDbAttributeValue.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1DynamoDbAttributeValue.java index 03d8034493db..dde41a3596cf 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1DynamoDbAttributeValue.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1DynamoDbAttributeValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1ItemFactory.java index 2d6d2870e655..f0273f388b7b 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1ItemFactory.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V1ItemFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2DynamoDbAttributeValue.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2DynamoDbAttributeValue.java index a68c3afa4905..93eaad9e4e71 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2DynamoDbAttributeValue.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2DynamoDbAttributeValue.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2ItemFactory.java index dbb4163bbbfa..3eaee2ea3030 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2ItemFactory.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/dynamodb/V2ItemFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1Ec2MarshallerBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1Ec2MarshallerBenchmark.java index dda717fb632f..d6eb7ac0b96e 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1Ec2MarshallerBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1Ec2MarshallerBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1ItemFactory.java index 2af350ecc803..c18545b6ae4c 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1ItemFactory.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V1ItemFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2Ec2MarshallerBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2Ec2MarshallerBenchmark.java index 585d88b5e305..37d789e7559e 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2Ec2MarshallerBenchmark.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2Ec2MarshallerBenchmark.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2ItemFactory.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2ItemFactory.java index 277a6ae0b053..d46f844bb121 100755 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2ItemFactory.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/marshaller/ec2/V2ItemFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkParams.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkParams.java index c68cdb136887..f419405bb690 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkParams.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkParams.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkResult.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkResult.java index b14ab23faf08..f974e3dc82bb 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkResult.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkResult.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkStatistics.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkStatistics.java index d665cc46a52c..643ad6118bbd 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkStatistics.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/stats/SdkBenchmarkStatistics.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/AlwaysSuccessServlet.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/AlwaysSuccessServlet.java index 528d736b7ed3..206e63eeda0a 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/AlwaysSuccessServlet.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/AlwaysSuccessServlet.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BaseMockServer.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BaseMockServer.java index 9a38bfbb14bb..65cdfafa353e 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BaseMockServer.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BaseMockServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkConstant.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkConstant.java index 7f6df3a74a23..c241ac25b4b3 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkConstant.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkConstant.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkUtils.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkUtils.java index 6be31ac3c0eb..3ef40c7af475 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkUtils.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/BenchmarkUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockH2Server.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockH2Server.java index b24f65ea46ee..5b1f20817940 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockH2Server.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockH2Server.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockHttpClient.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockHttpClient.java index 90947addac40..1f3f9d8d5853 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockHttpClient.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockHttpClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockServer.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockServer.java index d49b4a689068..15cca300789c 100644 --- a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockServer.java +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/utils/MockServer.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/resources/jetty-logging.properties b/test/sdk-benchmarks/src/main/resources/jetty-logging.properties index a3863a282b11..4ee410e7fa92 100644 --- a/test/sdk-benchmarks/src/main/resources/jetty-logging.properties +++ b/test/sdk-benchmarks/src/main/resources/jetty-logging.properties @@ -1,3 +1,18 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file 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. +# + # Set up logging implementation org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.LEVEL=OFF diff --git a/test/sdk-benchmarks/src/main/resources/log4j.properties b/test/sdk-benchmarks/src/main/resources/log4j.properties index b12d4e3360bc..e02a5de3252a 100644 --- a/test/sdk-benchmarks/src/main/resources/log4j.properties +++ b/test/sdk-benchmarks/src/main/resources/log4j.properties @@ -1,5 +1,5 @@ # -# Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. diff --git a/test/sdk-benchmarks/src/main/resources/software/amazon/awssdk/benchmark/baseline.json b/test/sdk-benchmarks/src/main/resources/software/amazon/awssdk/benchmark/baseline.json index aab954caa3b5..c09d97fbbfc4 100644 --- a/test/sdk-benchmarks/src/main/resources/software/amazon/awssdk/benchmark/baseline.json +++ b/test/sdk-benchmarks/src/main/resources/software/amazon/awssdk/benchmark/baseline.json @@ -2,361 +2,1864 @@ { "id": "apicall.httpclient.async.NettyClientH1NonTlsBenchmark.concurrentApiCall-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.539" + "date": "2020-03-18T20:11:42.308" }, "statistics": { - "mean": 11953.741893432696, - "variance": 47530.034301188265, - "standardDeviation": 218.01383970103427, - "max": 12372.362415239815, - "min": 11719.587724827139, + "mean": 11083.712145086858, + "variance": 8687.140893505537, + "standardDeviation": 93.20483299435463, + "max": 11201.90868543306, + "min": 10902.251879180842, "n": 10, - "sum": 119537.41893432697 + "sum": 110837.12145086858 } - }, - { + }, { "id": "apicall.httpclient.async.NettyClientH1NonTlsBenchmark.sequentialApiCall-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.544" + "date": "2020-03-18T20:11:42.314" }, "statistics": { - "mean": 3474.8929618663287, - "variance": 712.0377962331736, - "standardDeviation": 26.684036355716007, - "max": 3505.5433448106787, - "min": 3415.4832560499217, + "mean": 3133.078992847664, + "variance": 1068.4543621730531, + "standardDeviation": 32.687220165885215, + "max": 3168.0172510726, + "min": 3074.6431126456655, "n": 10, - "sum": 34748.929618663286 + "sum": 31330.78992847664 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH1Benchmark.concurrentApiCall-Throughput-sslProviderValue-jdk", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.544" + "date": "2020-03-18T20:11:42.314" }, "statistics": { - "mean": 9699.896431884776, - "variance": 76032.40025479376, - "standardDeviation": 275.73973281845645, - "max": 10206.843184671103, - "min": 9468.303441931195, + "mean": 9400.788325804802, + "variance": 1622.9542174492854, + "standardDeviation": 40.28590594053068, + "max": 9457.777847805768, + "min": 9341.909058431082, "n": 10, - "sum": 96998.96431884775 + "sum": 94007.88325804802 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH1Benchmark.concurrentApiCall-Throughput-sslProviderValue-openssl", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.544" + "date": "2020-03-18T20:11:42.314" }, "statistics": { - "mean": 10446.03380714306, - "variance": 66413.66363464146, - "standardDeviation": 257.7084857637432, - "max": 10923.23707364524, - "min": 10204.627718853411, + "mean": 10081.234880927226, + "variance": 2505.9361253161246, + "standardDeviation": 50.05932605735044, + "max": 10172.340399895851, + "min": 10022.546988640313, "n": 10, - "sum": 104460.3380714306 + "sum": 100812.34880927225 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH1Benchmark.sequentialApiCall-Throughput-sslProviderValue-jdk", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.544" + "date": "2020-03-18T20:11:42.315" }, "statistics": { - "mean": 2512.7905373200633, - "variance": 91.50695734966303, - "standardDeviation": 9.565926894434382, - "max": 2526.984094567836, - "min": 2495.5718551901746, + "mean": 2318.064309904416, + "variance": 1794.725237106914, + "standardDeviation": 42.36419758601494, + "max": 2362.020636579015, + "min": 2217.173827814984, "n": 10, - "sum": 25127.905373200632 + "sum": 23180.64309904416 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH1Benchmark.sequentialApiCall-Throughput-sslProviderValue-openssl", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.544" + "date": "2020-03-18T20:11:42.315" }, "statistics": { - "mean": 2784.4975432258934, - "variance": 450.46863826270305, - "standardDeviation": 21.22424647102231, - "max": 2818.4210467256917, - "min": 2744.7963740556124, + "mean": 2668.2980888540214, + "variance": 10194.040190854324, + "standardDeviation": 100.96553962047805, + "max": 2787.7737717880227, + "min": 2556.3548857046126, "n": 10, - "sum": 27844.975432258936 + "sum": 26682.980888540213 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH2Benchmark.concurrentApiCall-Throughput-sslProviderValue-jdk", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.544" + "date": "2020-03-18T20:11:42.315" }, "statistics": { - "mean": 7283.504799574257, - "variance": 2940.452504351831, - "standardDeviation": 54.22593940497325, - "max": 7341.573192234993, - "min": 7160.770039331679, + "mean": 6452.047990499835, + "variance": 844.6625223962324, + "standardDeviation": 29.063078336546393, + "max": 6499.658012694073, + "min": 6401.143996944477, "n": 10, - "sum": 72835.04799574257 + "sum": 64520.47990499835 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH2Benchmark.concurrentApiCall-Throughput-sslProviderValue-openssl", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.315" }, "statistics": { - "mean": 8384.82208375372, - "variance": 916.1282854797893, - "standardDeviation": 30.267611162425574, - "max": 8426.604455073824, - "min": 8321.092978907209, + "mean": 7299.549654768969, + "variance": 6802.210622023329, + "standardDeviation": 82.47551528801338, + "max": 7377.236551369815, + "min": 7142.628565890159, "n": 10, - "sum": 83848.22083753718 + "sum": 72995.49654768969 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH2Benchmark.sequentialApiCall-Throughput-sslProviderValue-jdk", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.315" }, "statistics": { - "mean": 2477.2975472033204, - "variance": 840.2618882518038, - "standardDeviation": 28.987271141861626, - "max": 2521.960405726022, - "min": 2428.703690483765, + "mean": 2253.2698214846414, + "variance": 440.12404468422363, + "standardDeviation": 20.979133554182443, + "max": 2277.64629879439, + "min": 2214.79534117503, "n": 10, - "sum": 24772.975472033202 + "sum": 22532.698214846412 } - }, - { + }, { "id": "apicall.httpclient.async.NettyHttpClientH2Benchmark.sequentialApiCall-Throughput-sslProviderValue-openssl", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.316" }, "statistics": { - "mean": 2590.8112289580376, - "variance": 454.61212848034586, - "standardDeviation": 21.321635220600363, - "max": 2616.8156380507344, - "min": 2540.5786144254553, + "mean": 2349.62389971199, + "variance": 605.1860304937383, + "standardDeviation": 24.600529069386663, + "max": 2387.296816018288, + "min": 2305.4407773762227, "n": 10, - "sum": 25908.11228958038 + "sum": 23496.238997119897 } - }, - { + }, { "id": "apicall.httpclient.sync.ApacheHttpClientBenchmark.concurrentApiCall-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.316" }, "statistics": { - "mean": 16866.337334610893, - "variance": 101462.03409398827, - "standardDeviation": 318.531056718161, - "max": 17190.064649926695, - "min": 16269.141724682142, + "mean": 15097.57607845867, + "variance": 765986.0501891866, + "standardDeviation": 875.2062900763377, + "max": 16056.36834170701, + "min": 14137.646966796729, "n": 10, - "sum": 168663.37334610894 + "sum": 150975.7607845867 } - }, - { + }, { "id": "apicall.httpclient.sync.ApacheHttpClientBenchmark.sequentialApiCall-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.316" }, "statistics": { - "mean": 4226.017857222098, - "variance": 3066.661807525882, - "standardDeviation": 55.37744854655081, - "max": 4305.988492552596, - "min": 4134.085450279892, + "mean": 3932.902248629381, + "variance": 3061.581931234147, + "standardDeviation": 55.331563607349345, + "max": 4034.253595323324, + "min": 3860.187186029488, "n": 10, - "sum": 42260.17857222098 + "sum": 39329.02248629381 } - }, - { + }, { "id": "apicall.httpclient.sync.UrlConnectionHttpClientBenchmark.sequentialApiCall-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.316" }, "statistics": { - "mean": 174.7664801674713, - "variance": 12.515295546405632, - "standardDeviation": 3.5376963615332553, - "max": 178.6770241537442, - "min": 166.3421471658005, + "mean": 769.724367683772, + "variance": 57.31368679644894, + "standardDeviation": 7.570580347400649, + "max": 788.1624697524128, + "min": 761.9236689968031, "n": 10, - "sum": 1747.6648016747129 + "sum": 7697.24367683772 } - }, - { + }, { "id": "apicall.protocol.Ec2ProtocolBenchmark.successfulResponse-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.545" + "date": "2020-03-18T20:11:42.317" }, "statistics": { - "mean": 9841.459076464273, - "variance": 10949.247996138049, - "standardDeviation": 104.63865440714558, - "max": 9965.24912712474, - "min": 9722.623015204204, + "mean": 9487.796808217518, + "variance": 574.9827758049166, + "standardDeviation": 23.978798464579427, + "max": 9527.963191787714, + "min": 9447.731608355678, "n": 10, - "sum": 98414.59076464272 + "sum": 94877.96808217518 } - }, - { + }, { "id": "apicall.protocol.JsonProtocolBenchmark.successfulResponse-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.546" + "date": "2020-03-18T20:11:42.317" }, "statistics": { - "mean": 16430.407523053836, - "variance": 29331.706560699487, - "standardDeviation": 171.26501849677152, - "max": 16638.092602848086, - "min": 16262.768049018146, + "mean": 15239.050304507653, + "variance": 5086.056215892704, + "standardDeviation": 71.31659144892375, + "max": 15346.02562533864, + "min": 15154.244391844419, "n": 10, - "sum": 164304.07523053835 + "sum": 152390.50304507653 } - }, - { + }, { "id": "apicall.protocol.QueryProtocolBenchmark.successfulResponse-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.546" + "date": "2020-03-18T20:11:42.317" }, "statistics": { - "mean": 10599.011117216372, - "variance": 975.0013823076044, - "standardDeviation": 31.225012126620616, - "max": 10644.281296039415, - "min": 10551.650476781753, + "mean": 10511.163793405529, + "variance": 11710.799056333717, + "standardDeviation": 108.21644540611061, + "max": 10633.736168153604, + "min": 10382.859111007954, "n": 10, - "sum": 105990.11117216373 + "sum": 105111.63793405528 } - }, - { + }, { "id": "apicall.protocol.XmlProtocolBenchmark.successfulResponse-Throughput", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "Throughput", - "date": "2019-10-01T20:07:52.546" + "date": "2020-03-18T20:11:42.317" }, "statistics": { - "mean": 9303.178155788462, - "variance": 21561.58218389355, - "standardDeviation": 146.83862633480862, - "max": 9463.863235819606, - "min": 9137.897225156119, + "mean": 8484.220376124444, + "variance": 27557.34797047023, + "standardDeviation": 166.00406010236685, + "max": 8658.753596062446, + "min": 8296.688244439016, "n": 10, - "sum": 93031.78155788462 + "sum": 84842.20376124444 } - }, - { + }, { "id": "coldstart.V2OptimizedClientCreationBenchmark.createClient-SampleTime", "params": { - "sdkVersion": "2.9.11", + "sdkVersion": "2.10.89", "jdkVersion": "1.8.0_222", "jvmName": "OpenJDK 64-Bit Server VM", "jvmVersion": "25.222-b10", "mode": "SampleTime", - "date": "2019-10-01T20:07:52.56" + "date": "2020-03-18T20:11:42.33" + }, + "statistics": { + "mean": 0.19604848685545748, + "variance": 15.024731435678909, + "standardDeviation": 3.876174845860144, + "max": 1572.864, + "min": 0.169216, + "n": 771613, + "sum": 151273.5610880001 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.enhanceGet-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.779" + }, + "statistics": { + "mean": 21861.411294887475, + "variance": 3163.9728423574056, + "standardDeviation": 56.24920303753117, + "max": 21931.163358835558, + "min": 21783.74564645071, + "n": 10, + "sum": 218614.11294887474 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.enhanceGet-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.798" + }, + "statistics": { + "mean": 19194.404041731374, + "variance": 1931.7305489412388, + "standardDeviation": 43.951456732868806, + "max": 19301.311007353008, + "min": 19154.59785578245, + "n": 10, + "sum": 191944.04041731375 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.enhanceGet-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.801" + }, + "statistics": { + "mean": 5742.760128972843, + "variance": 3812.4529097450204, + "standardDeviation": 61.7450638492262, + "max": 5818.06705484057, + "min": 5673.681434809584, + "n": 10, + "sum": 57427.60128972843 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.enhanceGet-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.803" + }, + "statistics": { + "mean": 9123.68471587034, + "variance": 39381.407506045354, + "standardDeviation": 198.44749307069958, + "max": 9324.8313949261, + "min": 8916.525076502701, + "n": 10, + "sum": 91236.8471587034 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.lowLevelGet-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.805" + }, + "statistics": { + "mean": 23727.653183389055, + "variance": 1302.5647136172183, + "standardDeviation": 36.091061408847736, + "max": 23790.896842117898, + "min": 23676.50791476011, + "n": 10, + "sum": 237276.53183389056 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.lowLevelGet-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.807" + }, + "statistics": { + "mean": 21204.570979007094, + "variance": 1805.7357256611479, + "standardDeviation": 42.49394928294083, + "max": 21261.432335576043, + "min": 21148.45805269659, + "n": 10, + "sum": 212045.70979007095 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.lowLevelGet-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.809" + }, + "statistics": { + "mean": 6631.846341687633, + "variance": 9642.265611299274, + "standardDeviation": 98.19503862873762, + "max": 6736.785800028154, + "min": 6531.270517561989, + "n": 10, + "sum": 66318.46341687633 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetOverheadBenchmark.lowLevelGet-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.811" + }, + "statistics": { + "mean": 10065.700621509586, + "variance": 5477.304848411613, + "standardDeviation": 74.00881601817187, + "max": 10148.047716366358, + "min": 9983.316684342379, + "n": 10, + "sum": 100657.00621509585 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.enhancedPut-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.813" + }, + "statistics": { + "mean": 23635.986227776833, + "variance": 95075.34657500139, + "standardDeviation": 308.3429042073149, + "max": 23938.47823573249, + "min": 23304.441614334046, + "n": 10, + "sum": 236359.86227776835 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.enhancedPut-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.816" + }, + "statistics": { + "mean": 20950.69006280451, + "variance": 883.8583335563803, + "standardDeviation": 29.729755020120503, + "max": 20994.25580029217, + "min": 20908.858362596562, + "n": 10, + "sum": 209506.9006280451 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.enhancedPut-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.818" + }, + "statistics": { + "mean": 6947.0547317414, + "variance": 2054.540956290902, + "standardDeviation": 45.3270444248343, + "max": 6992.094904618327, + "min": 6897.3604801922065, + "n": 10, + "sum": 69470.547317414 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.enhancedPut-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.819" + }, + "statistics": { + "mean": 9651.438384939946, + "variance": 21265.37256778877, + "standardDeviation": 145.82651531113527, + "max": 9810.960459690477, + "min": 9505.341211373996, + "n": 10, + "sum": 96514.38384939946 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.lowLevelPut-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.821" + }, + "statistics": { + "mean": 24474.133695525416, + "variance": 2418.272421611502, + "standardDeviation": 49.175933357807274, + "max": 24560.246261229124, + "min": 24391.482959504494, + "n": 10, + "sum": 244741.33695525417 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.lowLevelPut-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.823" + }, + "statistics": { + "mean": 21708.256095745754, + "variance": 33817.56629349679, + "standardDeviation": 183.89553092312164, + "max": 21896.648516991783, + "min": 21515.476349143923, + "n": 10, + "sum": 217082.56095745755 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.lowLevelPut-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.824" + }, + "statistics": { + "mean": 7831.76449879679, + "variance": 556.5114846450683, + "standardDeviation": 23.590495642208715, + "max": 7862.726711793453, + "min": 7801.301995363553, + "n": 10, + "sum": 78317.6449879679 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutOverheadBenchmark.lowLevelPut-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.826" }, "statistics": { - "mean": 0.11715317741749746, - "variance": 24.391494667328747, - "standardDeviation": 4.9387746119183, - "max": 3544.1868799999997, - "min": 0.09088, - "n": 1293869, - "sum": 151580.86451200003 + "mean": 10432.187037993292, + "variance": 6156.945597980833, + "standardDeviation": 78.4662067260858, + "max": 10544.74583687034, + "min": 10338.349564607815, + "n": 10, + "sum": 104321.87037993292 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Get-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.83" + }, + "statistics": { + "mean": 4216269.465030504, + "variance": 7577381680.455024, + "standardDeviation": 87048.1572490482, + "max": 4304995.187978772, + "min": 4127750.465031905, + "n": 10, + "sum": 42162694.65030504 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Get-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.831" + }, + "statistics": { + "mean": 2548116.917228338, + "variance": 39596645.65844414, + "standardDeviation": 6292.586563444649, + "max": 2553688.8961462937, + "min": 2536667.0775304707, + "n": 10, + "sum": 25481169.172283377 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Get-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.833" + }, + "statistics": { + "mean": 271517.73760595697, + "variance": 2372265.138141567, + "standardDeviation": 1540.2159388025975, + "max": 273137.05391642277, + "min": 269843.99771755247, + "n": 10, + "sum": 2715177.37605957 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Get-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.835" + }, + "statistics": { + "mean": 347920.5003151236, + "variance": 595046205.154461, + "standardDeviation": 24393.56893024186, + "max": 371195.38010237005, + "min": 324573.14439857507, + "n": 10, + "sum": 3479205.003151236 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Put-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.836" + }, + "statistics": { + "mean": 1768330.9260150697, + "variance": 1939469873.1268594, + "standardDeviation": 44039.412724590904, + "max": 1811149.0295745614, + "min": 1724510.13860136, + "n": 10, + "sum": 17683309.260150697 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Put-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.838" + }, + "statistics": { + "mean": 1164870.7035331992, + "variance": 52647682.835541315, + "standardDeviation": 7255.8723001126, + "max": 1174402.0970783627, + "min": 1155823.7219991074, + "n": 10, + "sum": 11648707.035331992 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Put-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.839" + }, + "statistics": { + "mean": 210052.38869182704, + "variance": 1798030.11996659, + "standardDeviation": 1340.9064545920382, + "max": 211619.30915467444, + "min": 208695.9982500961, + "n": 10, + "sum": 2100523.8869182705 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v1Put-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.841" + }, + "statistics": { + "mean": 114978.94801995096, + "variance": 1371813.7644851753, + "standardDeviation": 1171.244536587119, + "max": 116329.77786560146, + "min": 113730.43393826579, + "n": 10, + "sum": 1149789.4801995095 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Get-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.842" + }, + "statistics": { + "mean": 3662576.5648506857, + "variance": 7409482642.244276, + "standardDeviation": 86078.35176305525, + "max": 3748802.8826729953, + "min": 3575363.8664258076, + "n": 10, + "sum": 36625765.64850686 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Get-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.844" + }, + "statistics": { + "mean": 1646303.6313321758, + "variance": 1574901387.9625113, + "standardDeviation": 39685.027251628686, + "max": 1686779.9815694, + "min": 1607103.7820484997, + "n": 10, + "sum": 16463036.313321758 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Get-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.845" + }, + "statistics": { + "mean": 129737.87890043444, + "variance": 139476.8830297755, + "standardDeviation": 373.466039995306, + "max": 130343.97849463933, + "min": 129275.2611792706, + "n": 10, + "sum": 1297378.7890043445 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Get-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.847" + }, + "statistics": { + "mean": 276472.2259425583, + "variance": 2020459.345500282, + "standardDeviation": 1421.4286283525748, + "max": 278228.1324706078, + "min": 274914.59156783926, + "n": 10, + "sum": 2764722.2594255833 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Put-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.848" + }, + "statistics": { + "mean": 3971820.2835967117, + "variance": 17460612994.02266, + "standardDeviation": 132138.61280497332, + "max": 4108330.055204355, + "min": 3840104.0305961887, + "n": 10, + "sum": 39718202.835967116 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Put-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.849" + }, + "statistics": { + "mean": 1493615.511958485, + "variance": 9028125.181795087, + "standardDeviation": 3004.6838738534684, + "max": 1498786.274708349, + "min": 1488510.4353162742, + "n": 10, + "sum": 14936155.11958485 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Put-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.851" + }, + "statistics": { + "mean": 119057.84161286886, + "variance": 175279.77046631582, + "standardDeviation": 418.66426939292995, + "max": 119543.18118979972, + "min": 118539.26235554788, + "n": 10, + "sum": 1190578.4161286885 + } + }, { + "id": "enhanced.dynamodb.V1MapperComparisonBenchmark.v2Put-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.2-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-26T21:54:38.852" + }, + "statistics": { + "mean": 146022.84478369894, + "variance": 327890156.19659877, + "standardDeviation": 18107.737467629653, + "max": 163566.06331238395, + "min": 128721.90017507998, + "n": 10, + "sum": 1460228.4478369893 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v1Delete-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.606" + }, + "statistics": { + "mean": 6829142.946589122, + "variance": 4.8591555126432484E8, + "standardDeviation": 22043.492265617188, + "max": 6863185.870054239, + "min": 6799116.445357319, + "n": 10, + "sum": 6.829142946589121E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v1Delete-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.624" + }, + "statistics": { + "mean": 6808359.910172634, + "variance": 2.000208296099299E8, + "standardDeviation": 14142.87204247885, + "max": 6828977.335348215, + "min": 6786067.374609099, + "n": 10, + "sum": 6.808359910172634E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v1Delete-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.625" + }, + "statistics": { + "mean": 6760046.190670421, + "variance": 9.75658503627617E7, + "standardDeviation": 9877.542728976763, + "max": 6769841.418495995, + "min": 6737158.221512942, + "n": 10, + "sum": 6.760046190670422E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v1Delete-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.625" + }, + "statistics": { + "mean": 7063555.657198062, + "variance": 6.158453354962092E9, + "standardDeviation": 78475.81382159788, + "max": 7153269.815249109, + "min": 6982305.90267853, + "n": 10, + "sum": 7.063555657198063E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v2Delete-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.625" + }, + "statistics": { + "mean": 5218929.153482059, + "variance": 3.1478391403761997E9, + "standardDeviation": 56105.6070315276, + "max": 5273880.789650345, + "min": 5160076.774620201, + "n": 10, + "sum": 5.2189291534820594E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v2Delete-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.626" + }, + "statistics": { + "mean": 5269447.416256654, + "variance": 2.1756235980490078E10, + "standardDeviation": 147499.9524762299, + "max": 5418528.555564518, + "min": 5118992.6530110575, + "n": 10, + "sum": 5.269447416256654E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v2Delete-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.626" + }, + "statistics": { + "mean": 5233493.1041884385, + "variance": 2.4967218721697645E9, + "standardDeviation": 49967.20796852436, + "max": 5285980.226391762, + "min": 5183225.859548674, + "n": 10, + "sum": 5.2334931041884385E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientDeleteV1MapperComparisonBenchmark.v2Delete-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.626" + }, + "statistics": { + "mean": 5333879.183913028, + "variance": 3.8387871248915306E10, + "standardDeviation": 195928.2298417339, + "max": 5527130.395795353, + "min": 5146070.989876449, + "n": 10, + "sum": 5.333879183913028E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v1Get-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.632" + }, + "statistics": { + "mean": 4254457.087292329, + "variance": 4.95382992951288E8, + "standardDeviation": 22257.200923550292, + "max": 4275041.386231237, + "min": 4212061.286162632, + "n": 10, + "sum": 4.254457087292329E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v1Get-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.632" + }, + "statistics": { + "mean": 2541003.3761009574, + "variance": 4.786259780180736E9, + "standardDeviation": 69182.7997422823, + "max": 2611159.8604039275, + "min": 2472696.999220222, + "n": 10, + "sum": 2.5410033761009574E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v1Get-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.632" + }, + "statistics": { + "mean": 276072.8892665714, + "variance": 4934364.084565594, + "standardDeviation": 2221.3428561493142, + "max": 278598.68048084766, + "min": 273875.57772019814, + "n": 10, + "sum": 2760728.8926657136 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v1Get-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.632" + }, + "statistics": { + "mean": 334086.66986329446, + "variance": 472003.25447557855, + "standardDeviation": 687.024930024798, + "max": 334829.371548133, + "min": 333094.44110854296, + "n": 10, + "sum": 3340866.6986329444 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v2Get-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.632" + }, + "statistics": { + "mean": 3668470.1459464533, + "variance": 1.8762186153251028E9, + "standardDeviation": 43315.33926134139, + "max": 3720709.233930492, + "min": 3625326.4459771872, + "n": 10, + "sum": 3.6684701459464535E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v2Get-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.633" + }, + "statistics": { + "mean": 1705518.9560612484, + "variance": 1.4169945463025673E10, + "standardDeviation": 119037.58004523476, + "max": 1820514.4691287102, + "min": 1585840.1952959616, + "n": 10, + "sum": 1.7055189560612485E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v2Get-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.633" + }, + "statistics": { + "mean": 136996.69293126452, + "variance": 799817.5891452621, + "standardDeviation": 894.3252144188165, + "max": 138025.7200620876, + "min": 136055.1512978183, + "n": 10, + "sum": 1369966.9293126452 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientGetV1MapperComparisonBenchmark.v2Get-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.633" + }, + "statistics": { + "mean": 283351.0162156861, + "variance": 1.0298588244596072E7, + "standardDeviation": 3209.1413562814696, + "max": 286589.6531841922, + "min": 280136.4515638473, + "n": 10, + "sum": 2833510.1621568613 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v1Put-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.636" + }, + "statistics": { + "mean": 3989391.9219655544, + "variance": 3.417569254965252E8, + "standardDeviation": 18486.668858843263, + "max": 4010282.791681061, + "min": 3965936.2642360986, + "n": 10, + "sum": 3.989391921965554E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v1Put-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.637" + }, + "statistics": { + "mean": 2171253.7675951715, + "variance": 3.6857165241080485E7, + "standardDeviation": 6071.0102323320525, + "max": 2178298.2850115495, + "min": 2163119.833040153, + "n": 10, + "sum": 2.1712537675951716E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v1Put-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.637" + }, + "statistics": { + "mean": 244529.021162057, + "variance": 2376417.2502670567, + "standardDeviation": 1541.5632488701385, + "max": 246111.53410013518, + "min": 242947.81433874564, + "n": 10, + "sum": 2445290.21162057 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v1Put-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.637" + }, + "statistics": { + "mean": 176271.52763779167, + "variance": 5.385414480327478E7, + "standardDeviation": 7338.5383288005505, + "max": 183889.15456415596, + "min": 169198.1849380892, + "n": 10, + "sum": 1762715.2763779168 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v2Put-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.637" + }, + "statistics": { + "mean": 3980473.869357331, + "variance": 1.240006254392538E10, + "standardDeviation": 111355.56808676152, + "max": 4093377.541940185, + "min": 3863525.2091401936, + "n": 10, + "sum": 3.980473869357331E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v2Put-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.638" + }, + "statistics": { + "mean": 1537572.9568381808, + "variance": 2.4221168643443692E8, + "standardDeviation": 15563.151558551273, + "max": 1553592.9100167484, + "min": 1520555.0802638964, + "n": 10, + "sum": 1.5375729568381809E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v2Put-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.638" + }, + "statistics": { + "mean": 122705.30057333391, + "variance": 9716028.256289382, + "standardDeviation": 3117.0544198472667, + "max": 125727.08296444424, + "min": 119636.09183915379, + "n": 10, + "sum": 1227053.005733339 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientPutV1MapperComparisonBenchmark.v2Put-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.638" + }, + "statistics": { + "mean": 149741.39277360524, + "variance": 2.650470812611804E8, + "standardDeviation": 16280.266621317367, + "max": 165654.0707152279, + "min": 134059.54954768566, + "n": 10, + "sum": 1497413.9277360525 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v1Query-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.638" + }, + "statistics": { + "mean": 1252512.7584237454, + "variance": 3.138983828405156E9, + "standardDeviation": 56026.63499091442, + "max": 1307114.8285281677, + "min": 1197753.879018399, + "n": 10, + "sum": 1.2525127584237454E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v1Query-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.638" + }, + "statistics": { + "mean": 696709.8287589755, + "variance": 1020982.5317724581, + "standardDeviation": 1010.4368024633991, + "max": 697947.9778716824, + "min": 694879.5744542086, + "n": 10, + "sum": 6967098.287589755 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v1Query-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.638" + }, + "statistics": { + "mean": 87840.49274328267, + "variance": 910501.0389601943, + "standardDeviation": 954.20178105063, + "max": 88811.92214743672, + "min": 86825.8942896014, + "n": 10, + "sum": 878404.9274328267 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v1Query-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.639" + }, + "statistics": { + "mean": 98567.58634308925, + "variance": 512461.66059423453, + "standardDeviation": 715.8642752604956, + "max": 99409.22741703335, + "min": 97780.25847544703, + "n": 10, + "sum": 985675.8634308925 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v2Query-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.639" + }, + "statistics": { + "mean": 559019.8433378737, + "variance": 2.2290777068769585E7, + "standardDeviation": 4721.3109481127785, + "max": 564241.3270141733, + "min": 554165.3844559088, + "n": 10, + "sum": 5590198.433378737 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v2Query-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.639" + }, + "statistics": { + "mean": 333270.58140860766, + "variance": 2.1224538043119207E7, + "standardDeviation": 4607.009663883852, + "max": 338138.9613235646, + "min": 328731.43888478086, + "n": 10, + "sum": 3332705.8140860763 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v2Query-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.639" + }, + "statistics": { + "mean": 37840.02739826469, + "variance": 1.078376036635714E7, + "standardDeviation": 3283.863633946626, + "max": 40994.11267324832, + "min": 34707.83375925676, + "n": 10, + "sum": 378400.2739826469 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientQueryV1MapperComparisonBenchmark.v2Query-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.639" + }, + "statistics": { + "mean": 87298.81206391682, + "variance": 7797412.220043027, + "standardDeviation": 2792.3846833921407, + "max": 90058.97987038516, + "min": 84505.01102617542, + "n": 10, + "sum": 872988.1206391682 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v1Scan-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.639" + }, + "statistics": { + "mean": 1816952.3977204207, + "variance": 9.556202520739132E9, + "standardDeviation": 97755.83113420464, + "max": 1911328.3244720402, + "min": 1719772.0220435418, + "n": 10, + "sum": 1.8169523977204207E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v1Scan-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.64" + }, + "statistics": { + "mean": 967205.4743976349, + "variance": 1.3978351648120623E7, + "standardDeviation": 3738.7633848801697, + "max": 972027.7812119337, + "min": 962347.443999356, + "n": 10, + "sum": 9672054.743976349 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v1Scan-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.64" + }, + "statistics": { + "mean": 91879.33897708468, + "variance": 24930.40149699446, + "standardDeviation": 157.89363982439082, + "max": 92166.21433729483, + "min": 91599.279501587, + "n": 10, + "sum": 918793.3897708468 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v1Scan-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.64" + }, + "statistics": { + "mean": 116637.4994496019, + "variance": 158699.03677887947, + "standardDeviation": 398.37047679123947, + "max": 117246.77112486717, + "min": 116185.84450622648, + "n": 10, + "sum": 1166374.994496019 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v2Scan-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.64" + }, + "statistics": { + "mean": 2410421.1020664377, + "variance": 2.804147820060836E8, + "standardDeviation": 16745.58992708479, + "max": 2432181.1127997516, + "min": 2390376.4629096705, + "n": 10, + "sum": 2.410421102066438E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v2Scan-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.64" + }, + "statistics": { + "mean": 750074.5784853816, + "variance": 8.396679536063066E7, + "standardDeviation": 9163.339749274315, + "max": 759160.8566629316, + "min": 740079.8184763982, + "n": 10, + "sum": 7500745.784853815 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v2Scan-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.64" + }, + "statistics": { + "mean": 42561.66188409884, + "variance": 1.5191447556632824E7, + "standardDeviation": 3897.620755875669, + "max": 46321.96537040448, + "min": 38791.36393438352, + "n": 10, + "sum": 425616.6188409884 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientScanV1MapperComparisonBenchmark.v2Scan-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.641" + }, + "statistics": { + "mean": 96898.57710736312, + "variance": 553760.3709290832, + "standardDeviation": 744.1507716377664, + "max": 97657.81360405144, + "min": 96100.91050397762, + "n": 10, + "sum": 968985.7710736311 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v1Update-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.641" + }, + "statistics": { + "mean": 2661602.8279100014, + "variance": 1.2703172099024662E10, + "standardDeviation": 112708.34973073052, + "max": 2774023.757066647, + "min": 2548193.0483390293, + "n": 10, + "sum": 2.6616028279100016E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v1Update-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.641" + }, + "statistics": { + "mean": 1507250.535625762, + "variance": 5.655196829680228E8, + "standardDeviation": 23780.65774885175, + "max": 1532785.2783910045, + "min": 1483174.272510075, + "n": 10, + "sum": 1.5072505356257621E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v1Update-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.641" + }, + "statistics": { + "mean": 219532.91136539596, + "variance": 866308.2840868158, + "standardDeviation": 930.756834026383, + "max": 220708.66325981263, + "min": 218469.68829126397, + "n": 10, + "sum": 2195329.1136539597 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v1Update-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.641" + }, + "statistics": { + "mean": 145165.8982342992, + "variance": 6301083.691173323, + "standardDeviation": 2510.1959467685633, + "max": 147743.46373358855, + "min": 142678.49316236615, + "n": 10, + "sum": 1451658.982342992 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v2Update-Throughput-testItem-TINY", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.641" + }, + "statistics": { + "mean": 1335244.2925506658, + "variance": 9.767359974615078E8, + "standardDeviation": 31252.775836099867, + "max": 1365680.545124737, + "min": 1304606.9981228167, + "n": 10, + "sum": 1.3352442925506659E7 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v2Update-Throughput-testItem-SMALL", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.642" + }, + "statistics": { + "mean": 233466.6326390164, + "variance": 2551174.8672474767, + "standardDeviation": 1597.2397651096333, + "max": 235398.290118424, + "min": 231742.0022814813, + "n": 10, + "sum": 2334666.326390164 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v2Update-Throughput-testItem-HUGE", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.642" + }, + "statistics": { + "mean": 38060.01754411877, + "variance": 122853.00414046083, + "standardDeviation": 350.5039288516761, + "max": 38432.896736590395, + "min": 37715.230222863865, + "n": 10, + "sum": 380600.1754411877 + } + }, { + "id": "enhanced.dynamodb.EnhancedClientUpdateV1MapperComparisonBenchmark.v2Update-Throughput-testItem-HUGE_FLAT", + "params": { + "sdkVersion": "2.11.5-SNAPSHOT", + "jdkVersion": "1.8.0_242", + "jvmName": "OpenJDK 64-Bit Server VM", + "jvmVersion": "25.242-b08", + "mode": "Throughput", + "date": "2020-03-31T20:56:25.642" + }, + "statistics": { + "mean": 16831.633221796015, + "variance": 2523.4549523643527, + "standardDeviation": 50.23400195449645, + "max": 16909.85307725241, + "min": 16772.938959396386, + "n": 10, + "sum": 168316.33221796015 } } -] \ No newline at end of file +] diff --git a/test/service-test-utils/pom.xml b/test/service-test-utils/pom.xml index 945b629ee590..fbdbdc5d1a85 100644 --- a/test/service-test-utils/pom.xml +++ b/test/service-test-utils/pom.xml @@ -1,6 +1,6 @@